Invitation to Webinar: How to create mobile apps with Andromo

Tutorial: Using AirBop to Push Images for BigPictureStyle Notifications

Edit: If anyone is interested in the full source code please let me know.

Note: If you do not already have an Andromo account register now and start creating Android apps.

Introduction

This tutorial will show you how you can use AirBop to push images down to your Android apps and have the images displayed in the notification area. The main topics covered in this tutorial are:

This is an AirBop tutorial. This tutorial assumes that you are already a little familiar with Google Cloud Messaging, AirBiop, and the AirBop-Client sample. You don’t need to know everything about them, but understanding what they are and how they work will make this tutorial much easier to follow.

This tutorial also assumes that you already have an “AirBop account” (which is free to setup and free up to 1000 devices), and that you computer is properly setup for Android development using Eclipse.

Note: BigPictureStyle notifications are only supported on Android 4.1 and higher. On older versions of the operating system only the standard notification will be displayed.

Step 0 – Get the AirBop Client Sample Working

For this introductory step I’m just going to follow the AirBop-Client README documentation.

Specifically the Get the AirBop Sample Project, Opening the AirBop Sample Project in Eclipse, and Detailed Installation and Compilation Example sections. They show you exactly what you need to do in order to get the AirBop-Client setup.

Next we will need to create a new AirBop app. You can read more about this in the Adding a New App knowledge base article.

After following those two steps, you should have the AirBop-Client app in Eclipse, and an AirBop app that represents this tutorial. Now we need the to find the AirBop App Key, App Secret, and Google Project Number values and add them to the client. You can follow the Getting Started with AirBop tutorial to get these values. One you have them you will need to insert them in the the proper sections of the CommonUtilities.java file as detailed here.

Now you should have an AirBop-Client that will compile, and when you run on your Android device it will register with the AirBop servers and be ready to receive messages from the cloud.

App registered with AirBop

Step 1 – Adding the Image Download Code

Now we are ready to add the code that will download the image for us. For this we will use code posted to the Android Developers blog a few years ago under the title: Multithreading For Performance. We are going to use the first two code blocks posted: the downloadBitmap function and the FlushedInputStream class. For simplicity, and because of the way messages are received in the app, we will not be implemented the full solution detailed on the blog post.

We are going to put his code into a new class called: AirBopImageDownloader to do this select the package in the package explorer and then select File | New | Class in Eclipse’s main menu to bring up the new Java Class dialog. Fill it out as follows:

Copy and paste the two code sections from the Google blog into the new class. There are a few additional code changes that need to be made in order for the code to work.

First we need to add the required imports:

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.http.AndroidHttpClient;
import android.util.Log;

We also need to change the FlushedInputStream class so that the int bytes variable name is correct:

int byte = read();
if (byte < 0) {
  break;  // we reached EOF
} else {
  bytesSkipped = 1; // we read one byte
}

Becomes:

int num_byte = read();
if (num_byte < 0) {
  break;  // we reached EOF
} else {
  bytesSkipped = 1; // we read one byte
}

There is also an error with a Log call in the downloadBitmap function, so change:

Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e.toString());

To:

Log.w("ImageDownloader", "Error while retrieving bitmap from " + url + e.toString());

Finally we need to use the FlushedInputStream (as explained in the blog post) when we create the Bitmap by changing:

final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

To:

final Bitmap bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));

When all is said and done the class will look like the following:

package com.airbop.client;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.http.AndroidHttpClient;
import android.util.Log;

public class AirBopImageDownloader {
	
	static public Bitmap downloadBitmap(String url) {
	    final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
	    final HttpGet getRequest = new HttpGet(url);

	    try {
	        HttpResponse response = client.execute(getRequest);
	        final int statusCode = response.getStatusLine().getStatusCode();
	        if (statusCode != HttpStatus.SC_OK) { 
	            Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url); 
	            return null;
	        }
	        
	        final HttpEntity entity = response.getEntity();
	        if (entity != null) {
	            InputStream inputStream = null;
	            try {
	                inputStream = entity.getContent(); 
	                final Bitmap bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
	                return bitmap;
	            } finally {
	                if (inputStream != null) {
	                    inputStream.close();  
	                }
	                entity.consumeContent();
	            }
	        }
	    } catch (Exception e) {
	        // Could provide a more explicit error message for IOException or IllegalStateException
	        getRequest.abort();
	        Log.w("ImageDownloader", "Error while retrieving bitmap from " + url + e.toString());
	    } finally {
	        if (client != null) {
	            client.close();
	        }
	    }
	    return null;
	}
	
	static class FlushedInputStream extends FilterInputStream {
	    public FlushedInputStream(InputStream inputStream) {
	        super(inputStream);
	    }

	    @Override
	    public long skip(long n) throws IOException {
	        long totalBytesSkipped = 0L;
	        while (totalBytesSkipped < n) {
	            long bytesSkipped = in.skip(n - totalBytesSkipped);
	            if (bytesSkipped == 0L) {
	                  int num_byte = read();
	                  if (num_byte < 0) {
	                      break;  // we reached EOF
	                  } else {
	                      bytesSkipped = 1; // we read one byte
	                  }
	           }
	            totalBytesSkipped += bytesSkipped;
	        }
	        return totalBytesSkipped;
	    }
	}
}

Step 2 – Responding to the Message and Downloading the Image

When we send the messages from AirBop we are going to be using JSON to pass our data. To read that JSON (now in the form of an Intent Bundle) we are going to have to edit the onMessage function in the GCMIntentService class. There we are going to define a new String variable that will store the image URL and then read that data out of the bundle. Once we have the data we will then send it to a new function called generateImageNotification which will be a duplicate of the generateNotification function with some our additional image handing:

String image_url = null;
...
image_url = bundle.getString("image_url");
...
generateImageNotification(context, title, message, url, image_url); 

The onMessage function will now look like the following:

@Override
protected void onMessage(Context context, Intent intent) {
	
	Log.i(TAG, "Received message");
	displayMessage(context, "Message Received" );
	String message = null;
	String title = null;
	String url = null;
	String image_url = null;
	
	if (intent != null) {      	
		//Check the bundle for the pay load body and title
		Bundle bundle = intent.getExtras();
		if (bundle != null) {
			displayMessage(context, "Message bundle: " +  bundle);
			
			Log.i(TAG, "Message bundle: " +  bundle);
			message = bundle.getString("message");   			 	   		
			title = bundle.getString("title");	
			url = bundle.getString("url");	 	   		
			image_url = bundle.getString("image_url");
		} 
	}
	// If there was no body just use a standard message
	if (message == null) {
		message = getString(R.string.airbop_message);
	}	   	   		
	generateImageNotification(context, title, message, url, image_url); 	
}

Next we need to actually duplicate the generateNotification function (the one with the URL parameter), change the name, and add the additional image_url parameter:

private static void generateImageNotification(Context context
		, String title
		, String message
		, String url
		, String image_url) 

The first task for our new function is to attempt to download the image using our AirBopImageDownloader class. If we cannot download the image then we are going to simply call the default generateNotification function and return:

// The bitmap to download
Bitmap message_bitmap = null; 
// Should we download the image?
if ((image_url != null) && (!image_url.equals(""))) {
	message_bitmap = AirBopImageDownloader.downloadBitmap(image_url);
}
// If we didn't get the image, we're out of here
if (message_bitmap == null) {
	generateNotification(context
			, title
			, message
			, url);
	return;
}

Step 3 – Creating the BigPictureStyle Notification

This step is actually very easy since we have all the pieces that we need and the AirBop-Client sample already uses the new Jelly-Bean style notifications. All we have to do is adjust the notification creation code to use the BigPictureStyle instead of the BigTextStyle and to pass it our already downloaded bitmap:

 Notification notification = new NotificationCompat.Builder(context)
		.setContentTitle(title)
		.setContentText(message)
		.setContentIntent(intent)
		.setSmallIcon(icon)
		.setWhen(when)
		.setStyle(new NotificationCompat.BigPictureStyle()
			.bigPicture(message_bitmap))
	.build();

That’s it. Now the generateImageNotification function is complete. Here is it in its entirety:

private static void generateImageNotification(Context context
		, String title
		, String message
		, String url
		, String image_url) {
	// The bitmap to download
	Bitmap message_bitmap = null; 
	// Should we download the image?
	if ((image_url != null) && (!image_url.equals(""))) {
		message_bitmap = AirBopImageDownloader.downloadBitmap(image_url);
	}
	// If we didn't get the image, we're out of here
	if (message_bitmap == null) {
		generateNotification(context
				, title
				, message
				, url);
		return;
	}
			
	int icon = R.drawable.ic_stat_gcm;
	long when = System.currentTimeMillis();
	NotificationManager notificationManager = (NotificationManager)
			context.getSystemService(Context.NOTIFICATION_SERVICE);
	
	if ((title == null) || (title.equals(""))) {
		title = context.getString(R.string.app_name);
	}
	
	Intent notificationIntent = null;
	if ((url == null) || (url.equals(""))) {
		//just bring up the app
		notificationIntent = new Intent(context, DemoActivity.class);
	} else {
		//Launch the URL
		notificationIntent = new Intent(Intent.ACTION_VIEW);
		notificationIntent.setData(Uri.parse(url));
		notificationIntent.addCategory(Intent.CATEGORY_BROWSABLE);
	}
	
	// set intent so it does not start a new activity
	notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
			Intent.FLAG_ACTIVITY_SINGLE_TOP);
	PendingIntent intent =
			PendingIntent.getActivity(context, 0, notificationIntent, 0);
			
	Notification notification = new NotificationCompat.Builder(context)
			.setContentTitle(title)
			.setContentText(message)
			.setContentIntent(intent)
			.setSmallIcon(icon)
			.setWhen(when)
			.setStyle(new NotificationCompat.BigPictureStyle()
				.bigPicture(message_bitmap))
		.build();
	
	notification.flags |= Notification.FLAG_AUTO_CANCEL;
	notificationManager.notify(0, notification);
}

Step 4 – The Message

That’s it for the code, the app now does everything that we want it to, so the next step is to compose a message and sent it to our app. To do this we need to send a new message on AirBop, the “Sending a New Message” knowledge base article explains the basics of how to do this.

Our message is going to be a JSON message so the title we use for our message will just be a reference. We’ll set the title to “First Image Push”, and then set the “Mode” to JSON. This example message is going to send the AirBop logo down to the app. The JSON to do this looks very similar to the JSON in the <“Sending a New Message” knowledge base article, except that the JSON values are different and we added the image_url parameter:

{
    "title": "AirBop",
    "message": "Here is your BigPictureStyle notification",
    "url": "http://www.airbop.com",
    "image_url" : "https://www.andromo.com/wp-content/uploads/uploads_v1/2012/12/a.1003-small-size.jpg"
}

The JSON should be pretty self explanatory, it is sending a standard AirBop message (title, message, url) down to the app with the additional URL of the image that we will use in the BigPictureStyle notification. Notice that the image_url parameter corresponds with the string we used to load the image URL out of the bundle in the onMessage function earlier:

image_url = bundle.getString("image_url");

So let’s send this message now, by setting the Send at value to Now and pressing that lovely blue Send Message Now button at the bottom of the Send Message page.

Step 5 – The Result

Now that we have sent the message we can relax and wait for it to arrive in the next few milli-seconds. Once it does we should see the following:

When the user clicks on the image they will be sent to the url parameter that we passed in the JSON.

If the user shrinks the notification then they will see our message text:

That’s it! Now you know one method for adding pictures to your AirBop push notifications. Good luck! Feel free to leave a question or a comment.

Share:

Share on facebook
Facebook
Share on twitter
Twitter
Share on pinterest
Pinterest
Share on linkedin
LinkedIn

17 Responses

  1. That awkward moment when this can’t be done on Andromo. :/

    You know what I am about to ask…is this an update coming soon? 😉

    1. Hi Timmy, right now we don’t have any plans on integrating this type of support into Andromo, however since we control the Andromo code it would not be that difficult to integrate something in the future. One issue is the fact that it uses custom JSON which would be very easy for people to break.

    1. Rony, it all depends on your content and what your app is doing. If your app is a photography app, or an app that delivers a photo of the day, it might be a desirable way to let people know. But you are correct, if all you are doing is pushing ads down to your users they may get upset.

        1. Once a week might be too often, once a month might be more acceptable. The only issue is that notification ads are generally frowned upon by most users.

    1. Currently this code is not in Andromo, however it is something that we are looking at adding in a future Andromo release.

        1. Do you mean the new Dashboards? If so we are still working on those. Watch the blog, we will post information on them as we are finished.

  2. Hi!, I do what you say, but in my galaxy S2 (android 4.1.2) the image (BigPicture) doesn’t display. What could happen?
    I could see the message as a notification and I can get the bitmap, but it doesn’t display.

    1. Hi Claudio,

      What do you see as your notification? You might have to use the two-finger swipe to expand the notification (I forget what happens on a 4.1 phone).

      As long as you have the following code in your notification creation and the bitmap is valid (e.g. not null):

      .setStyle(new NotificationCompat.BigPictureStyle()
      .bigPicture(message_bitmap))

      You should be seeing big style notifications. If you are still having issues you can open a support ticket on the airbop site: http://support.airbop.com

  3. In step 5 you mentioned a few milli-seconds for delivery a message.
    My experience with the latency of GCM messages is several seconds and not milli-seconds.
    But an Android notification with an image is very interesting for me and I was wondering if I can get the full source code. I can compile the default AirBop client.

  4. When I send a Message , at Activity , There is a textview “Message bundle : Bundle[{image_url=https://www.andromo.com/blog/~” , not Image Notification.

    And at the status bar , there is a notification not an image notification that Air Bop , Here is your BigPictureStyle notification.

    Please let me know what trouble I have. Please

  5. So please does it means all my app users must also be running a compiled AirBop-Client on their phone before they can receive BigPictureStyle Notifications from me?

Comments are closed.

On Key

Related Posts

passive-income-ideas

5 Passive Income Ideas

If you’ve been looking for smart ways to make money in 2020 and beyond, this article is for you. At Andromo, we’re all about making

How to create mobile apps with Andromo

January 25

We will talk about:

Mobile app market overview

Successful Andromo apps

Andromo Mobile builder overview

We will answer your questions

Want participate?