Android App Store Survey Results

A while ago I posted a survey for Android developers trying to determine which app stores were the most important to them and while the response wasn’t overwhelming (only 103 responses) I thought I would share some of the results that we have collected so far:

Which app stores have you uploaded your app(s) into?

UploadedStores

Click on the image for a larger view. The most popular app store (by a long shot) is Google Play. Here are the five most popular app stores in the survey:

  1. Google Play – 91
  2. Amazon AppStore – 52
  3. Samsung Apps – 27
  4. Opera Mobile Store – 23
  5. Yandex – 14

What Are Your Top Five App Stores?

Not much different in the distribution people gave when they votes for their top five app stores:

TopFive

  1. Google Play – 94
  2. Amazon AppStore – 53
  3. Samsung Apps – 32
  4. Opera Mobile Store – 24
  5. SlideME – 15

The only difference in the top five is last spot where SlideME replaces the Yandex store. I think that this has to do with the fact that SlideME does not allow Andromo apps, and since many of the respondents were Andromo developers they might feel that SlideME is an important app store but have not been able to upload their apps into SlideME just yet.

How important are alternative app stores to you as an Android developer?

HowImportant

Here we can see a pretty even split across all five responses with a majority of developers feeling that alternative app stores are important in some way.

Conclusion

Sadly the number of responses are too small to really get a clue as to how important alternative app stores are to Android developers, but I think that we can conclude a few things from the results:

  1. Google Play is important. Google Play is by far the most important app store to Android developers who responded, which makes sense given that it is considered the premier app store and the default App store for most Android phones sold outside of China.
  2. Alternative App Stores are important. Given the number of different app stores that respondents distributed their apps into I think you can safely say that alternative app stores are important to Android developers. After all most of these stores are free, and if you can get a few more downloads with minimal effort why not?

So what do you think? Do you agree with the survey results? Have you changed your mind since you answered the survey? Let me know in the comments below.

Note: These results would probably have been much different if we could have surveyed Chinese app developers.

Welcome to Andromo 4.0!

If you are reading this then there is a good chance you already know that andromo.com has been updated. If you didn’t know about the update, stop reading and go check out the main site right now. When you are done come back and we’ll continue with the rest of this post.

One of the main reasons for the website redesign was to have a more responsive site that looks great and works great on both your computer and on your mobile device. Of course you will probably be doing most of your work on a computer with a keyboard and mouse, but we wanted to make it easier for you to do more on andromo.com no matter where you are.

Let’s take a look at a few comparisons between the old site and the new site on a Nexus 5 Android phone:

Main Home Screen

Old Site

New Site

Creating a New Project

Old Site

New Site

New RSS Activity

Old Site

Old Site

New Site

Now the Desktop

What about on the desktop, let’s see how the RSS Activity compares:

Old Site



New Site

I think you get the idea. If screenshots don’t do it for you, here is the “Creating your first app” video for the new website:

Now head on back to andromo.com and start making a new Android app!

P.S. If there is something broken in any of the activities on the website, that’s probably my fault, so be kind when you report the bugs!

Andromo App Maker for Android v4.0.0 Released

Andromo App Maker for Android version v4.0.0 has been released.

Here is the list of changes in this version:

  • Complete Andromo.com redesign with a more responsive and modern design. Read More
  • Updated the platform SDK and underlying build tools to version 19.
  • Updated the map activity so it uses the newest API and changed the font size of some info window items for more consistency.
  • Updated the Amazon Mobile Ads SDK to the latest version: 5.1.153.
  • Made it so the audio service is started with explicit intents.
  • Made some minor changes to how AdMob and Millennial Media Banner ads are shown in order to shorten the time the banner area is visible before an ad is available.
  • Made Amazon Mobile Ads fill the device width while in portrait orientation.
  • Made it so that the length of image file names, uploaded into the Custom Page editor, are truncated if they are longer than the 100 character length allowed by Android.
  • Added a workaround for an undocumented change in the MediaPlayer behaviour in Android 4.x.
  • Added support for xxxhdpi application icons.
  • Fixed an issue where the dashboard header image was not being sized correctly when certain ad networks were enabled.
  • Fixed possible ANR if the MediaPlayer was reset while it is in the Preparing state.
  • Fixed potential crash if the Audio service was restarted by the system after an ANR.
  • Fixed an issue in the Website activity where clicking on some links in a specific format would result in a blank page being shown on some versions of Android.
  • Minor fix to tel: link handling in the WebView if the dialer is not available.

Andromo App Maker for Android v4.0.0 is now live at www.andromo.com.

Ch-ch-changes are coming to Andromo.com

New Andromo Website

We first took Andromo live on August 24, 2011 and since then we’ve added a metric tonne of new features and options, but during that time the website has remained pretty much the same. In the coming days or weeks that’s about to change.

Colin has been (very) hard at work the last little while bringing our website into the modern age. And it’s not just a change here or a change there; this is change from the top to the bottom. Even yours truly has even got his client-side hands dirty after learning a little bit of Ruby on Rails at the end of last year. (So…when we launch if you see a problem when you are editing one of your activities that’s probably my fault.)

The new website has three major goals (in no particular order):

  • Responsive Design – We want Andromo.com to look great no matter how you view it. Mobile, Desktop, Tablet, whatever.
  • Modern Look – Times and styles change, and we wanted to modernize the look of the site in the same way we did the app early in 2013.
  • Better User Experience – This one is sort of a given, and the other two go a long way to improve the user experience on our site, but we wanted to make using Andromo better.

Of course this website update will not address every feature request that has been made for the site, but it will set the ground work for a better experience and easier updates going forward.

Now if you’ll excuse me I need to get back to work on updating the Map Activity’s web interface:

Update Map Activity

Time may change me
But I can’t trace time
I said that time may change me
But I can’t trace time
Changes – David Bowie

Getting Started With Andromo App Maker

The 10,000 Foot View of Andromo.com

Andromo.com represents the true democratization of the app creation process, it is a website that lets anyone transform their idea into an Android app. In order to create your Android app using Andromo, just follow these three steps:

1. Create a project: Each project you create within your Andromo account represents one android app. To create an Android app using Andromo, you need to create a project.
2. Add activities to your project: Activities are what give your app functionality. In order for your app to do *anything* you need to add activities to it.
3. Build your Project: Building your project will generate your Android app. When you are done, or when you want to test, you need to build your project via the Build tab. This will generate an APK file which you can then install on your Android device.

Note: Before you can create an app using Andromo you need to sign up for a valid Andromo account. If you don’t have one already visit Andromo.com to register for free.

You can also watch this short video which will walk you through the process: Creating Your First Android App using Andromo.com

Andromo In More Detail

Projects

As mentioned above, a project within Andromo represents an Android app. If you want to create an Android app using Andromo you need to create a project first.

To create a project, log into your Andromo account and visit your projects page.

There you will see a large green button that you should click on to create a project:

After you click this button you will be prompted to name your project. The name of your project will be what your end users sees on their Android device, but don’t be too concerned with your project name to start, as you can always change it later.

Activities

Activities are what give your Android app functionality. Without any activities your Android app won’t do anything, so the first thing that you want to do after creating your project is go to the Activities tab.

There are 19 different activity types that you can use to add functionality to your app, from the About Activity to the YouTube Activity. These activities are the tools that you have at your disposal when creating your app. You can add as many or as few activities as you want to your app, but a good safe bet is at least four activities per app.

See the Activities Knowledge Base section for more information on different activity types available to you and how you can make use of them.

Building

Building is the process of transforming your Andromo project into an Android APK. An Android APK is a single file that represents your Android app. This is the file that your end user will install onto their Android device and then execute in order to use your app. When you are satisfied with your app, or when you want to test what you have created so far, you need to visit the build tab to generate your Android APK.

Building your project is very easy, simply click the big green ‘Generate My App!’ button on the Build tab, and Andromo will start building your app.

Once the build process has completed you will be emailed a link to your Android APK file and the download link will be made available within your Andromo.com dashboard.

Once you have the link to your Android APK you can install your app on your Android device and publish your app to Google Play.

Helpful Links

Here are some links that will help you make the most out of Andromo:

Andromo App Maker for Android v3.2.8 Released

Andromo App Maker for Android version v3.2.8 has been released.

Here is the list of changes in this version:

  • Added a Save Image option to the Flickr and RSS activity (in Photo Feed mode) on Android 2.3 and up.
  • Changed the “Set as wallpaper” option in the Photo Feed to be “Set picture as”. This gives the end user more control over what they can do with the photo. This allows the end user to crop the photo or even use it as a contact photo. This is available in Android 2.3 and up, older versions of Android will use the old “Set as wallpaper” functionality.
  • Moved the “Set Picture as” option from the overflow menu onto the action bar as an icon.
  • Added the ability for developers to select an Alternate package name for their app on the App Info tab
  • Removed and disabled Pingjam as a monetization option due to policy changes at Google Play. We recommend that anyone who was using Pingjam as a monetization option in their app should rebuild and update in Google Play immediately.

Andromo App Maker for Android v3.2.8 is now live at www.andromo.com.

Using the New Andromo 3.0 Dashboard and Action Bar

I’ll put a girdle round about the earth
In forty minutes. (Or eight months…)
― Puck, A Midsummer Night’s Dream

It’s here, it’s finally here, after months and months of work and a few blog posts Andromo 3.0 has been released. There have been a lot of changes, I won’t go over all of them, but if you are interested I suggest you read our changelog.

But what do these new changes really mean to you, the Andromo developer? In my opinion it means the following:

  • You can make your apps look different.
  • You can make your apps look better.
  • You can customize your dashboard.
  • You can achieve looks and functionality that were impossible before.
  • Your app will look much more modern and fit into the Holo theme.

In a nutshell: you can do more and your apps will look better than they did before. I think it’s a pretty big change. This blog post will show you some examples of what is possible with the new version of Andromo and hopefully spark some ideas for you to use in your own apps.

Glossary

Before going on I wanted to highlight some of the terms that I will be using in this blog post. Rather than describe what these terms mean, I decided to let some screen shots do the talking:

All of the above screenshots are from our knowledge base, if you want any more information on any of the terms, you should find your answer there.

Examples

Here is an example of a dashboard that makes use of the Gallery dashboard type for a fictitious Pablo Picasso app:

“Computers are useless. They can only give you answers.”
― Pablo Picasso

The dashboard settings are pretty straight forward:

  • A full background meant to resemble a paint canvas.
  • Large square activity icons all close to the 256×256 limit.
  • Short activity titles.
  • Transparent image for the banner image.
  • Panel alignment set to middle.
  • Panel background visibility set to transparent.

Here is the same app in landscape mode:

The landscape look is achieved with the same settings as above except that the landscape banner image has been rotated vertically.

One improvement to the dashboards that I am quite proud of was implemented after version 3.0 of the dashboards was released, and wasn’t available to the public until version 3.0.3. The change I’m referring to was a change to the font style used for the Button Grid and List style dashboard. The change isn’t massive but I think that the results look a lot better.

After releasing we realized that when using both the List and Dashboard dashboard types there was some extra room for the activity title text, especially when the subtitles were disabled. Here’s a screen shot that shows the changes we made:

As you can see there is quite a difference between the three looks, which is why one of my favourite dashboard looks is to use is either the Button Grid or List type, over a photo background, with a transparent panel, and disabled subtitles:

“As long as there are games to play it is not over.”
– Sir Alex Ferguson

Settings:

  • Button Grid dashboard type.
  • Photo background, no frosting and not tiled.
  • Panel background invisible.
  • Show Subtitle Text panel setting unchecked.

Of course that isn’t to say that I don’t like the new subtitle feature, in fact I think that it can be very useful, but the use case really depends on the app that you are making. Here’s an example that uses the subtitle to what I think is good effect:

“Nothing happens to anybody which he is not fitted by nature to bear.”
― Marcus Aurelius, Meditations

Settings:

  • Dashboard type List.
  • Banner image in portrait mode, size: fit to image.
  • Panel background solid with custom colours.
  • Custom logo image in action bar.
  • Action bar display mode: No text or activity navigation.

Of course it’s still alright to have a little fun with your dashboard:

“Oh, you’re a candy maker who lives with short, orange men and never goes outside? Please share with me your criticisms on the nuances of modern society.”
― Condescending Wonka

Settings:

  • ‘Feel Like a Sir’ banner image.
  • Nyan cat background.
  • Panel background invisible.
  • Custom logo image in action bar.
  • Action bar display mode: No text or activity navigation.

Here’s one last idea that I was toying around with as well that I think could be used to good effect:

The pump don’t work
’Cause the vandals took the handles
― Bob Dylan, Subterranean Homesick Blues

As you can see, you can do a lot more with the new dashboard then you could before, and with a little bit of creativity, and some basic graphic skills, the results can be quite beautiful. Rather than simply forgetting about the dashboard and hitting build once you’ve added your activities, spend some time on the dashboard I think your users and your install numbers will thank you.

And now for the weekend…

If we shadows have offended,
Think but this, and all is mended,
That you have but slumbered here
While these visions did appear.
― Puck, A Midsummer Night’s Dream

New Dashboard – Final Testing

We’re almost there, the new Dashboard is almost done and has entered into the “final testing” phase.

“What does this mean?” you ask.

It means that we’re very busy, it also means that the new Dashboard and all of the other changes we’ve been making (e.g. the lovely new Action bar pictured above) will be released very soon.

“How soon?”

Maybe as early as next week.

“Really?”

Really.

“What should I do?”

Well the changes that are coming to Andromo with this update are quite vast and will change the way your app looks. Some of the settings that were previously available in Andromo are making way for newer and better settings. This means that your app will look differently when you build it after we unleash this update. So if you really like the way your Andromo app looks now, if you have a dashboard you love, we recommend that you build it one last time before we throw the switch.

We think that it will change your apps for the better, and that you are going to be able to create looks that were impossible before, but there will be a change.

Other than that, sit back, relax, and get ready to update your apps.

“Anything else?”

Enjoy your weekend, it’s a long weekend up here in Winnipeg, so we’ll see you on Tuesday.

Oh and here’s that last app on a Nexus 7.

And one final screenshot just for fun.

Ok, fine, just one more for Lorne.

Sneak Peek: New Dashboard Button Grid Style

In late October I posted a sneak peek look at the New Dashboard’s None Style. This week I want to take a look at the new Button Grid style.

I’m not going to go too deep into this one, since it’s similar to the other new dashboard types, but a few screen shots won’t hurt.

Button Grid Style

The Button Grid style is influenced by one of the more familiar layouts in the Google Play app:

What we like about this layout is its ability to show a lot of data in a visually appealing way. It provides room for the activity icons, activity title text, and activity sub-title text without being cluttered and without taking up too much room.

So let’s look at an example app using this new Dashboard style:

If you have read the previous posts about the new Dashboard styles you’re already familiar with what the Button Grid style can do in terms of portrait vs. landscape, logo area, icons, etc. so I won’t go into it here. But I did want to point out how powerful the new Dashboard styles are. They let you create very different looks without you the Andromo developer having to change too many settings.

Let’s take a look at the Button Grid in portrait mode:

So that’s it for this short sneak peek. We’re working hard on all of these features so keep watching the blog for updates. Speaking of features did anyone notice anything interesting about the action bar?

Note: As I mentioned in the previous “new dashboard” posts, what you are seeing here is still being worked on. That means that the final version may end up being a little different.

Tutorial: Using AirBop to Send Images in the Message Payload

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

Introduction

This AirBop tutorial builds on the code that was written for the Using AirBop to Push Images for BigPictureStyle Notifications tutorial. As in the last tutorial we will be pushing an image down to our client app, but instead of getting the app to download the image, this time we will be including the image data as part of the message payload.

The main topics covered in this tutorial are:

  • How to base64 encode an image.
  • How to include a base64 encoded image as part of your AirBop message payload.
  • How to decode the base64 string into image in the client app.
  • How to use the setLargeIcon() method when creating a notification.

As brief discussion on the difference between “Send-to-Sync” and “Messages with Payload” can be found in Google’s documentation.

Message Overview

In general this is how sending the image as part of the payload will work:

  1. Convert the image into a base64 encoded string.
  2. Send a message from AirBop with that base64 string included, to the Google Cloud Messaging (GCM) servers.
  3. The GCM servers deliver that message to the app.
  4. The app receives the message and decodes the base64 string back into an image.
  5. The app then uses the image in the notification it creates.

Step 0 – Using the Code from the Previous Tutorial

This tutorial won’t go over getting the AirBop-Client sample code into Eclipse and setting up your AirBop account. For that I refer you to the last tutorial. The code written for the last tutorial will be the starting point for this tutorial, so if you are just reading this tutorial I suggest you give the previous tutorial a read (or just grab the code) and then come back.

Step 1 – Reading the Encoded Image from the JSON

As with the last tutorial we will be using custom JSON when we send our message. We will have to edit the onMessage function in the GCMIntentService class. This code is pretty straightforward and basically a duplicate of similar code written in the previous tutorial:

String large_icon = null;
...
large_icon = bundle.getString("large_icon");

The difference this time is that we will use the existence of the image_url data to determine which function to call:

if (image_url != null) {
	generateImageNotification(context, title, message, url, image_url, large_icon); 	
} else {
	generateNotification(context, title, message, url, large_icon);
}

So if we get the image_url data we will create a bigPictureStyle Notification. If we don’t we will call the generateNotification method. Both methods will get the large_icon data passed to them. The OnMessage method now looks 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;
    String large_icon = 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");
 	   		large_icon = bundle.getString("large_icon");
 	   	} 
    }
   	// If there was no body just use a standard message
   	if (message == null) {
   		message = getString(R.string.airbop_message);
	}
 
   	if (image_url != null) {
   		generateImageNotification(context, title, message, url, image_url, large_icon); 	
   	} else {
   		generateNotification(context, title, message, url, large_icon);
   	}
}

Step 2 – Creating the decodeImage() Function

In this step we will create the decodeImage() function that we will call to decode the base64-encoded image string back into a bitmap. The function is quite straightforward, it accepts a string as it’s only parameters and then, if the string is valid, it will attempt to base64 decode the image data. If the data is successfully decoded then it will attempt to create a Bitmap from it and then return that Bitmap:

/**
 * Decode a base64 string into a Bitmap
 */
private static Bitmap decodeImage(String image_data) {
	// Decode the encoded string into largeIcon
	Bitmap largeIcon = null;
	if ((image_data != null) && (!image_data.equals(""))) {
		byte[] decodedImage = Base64.decode(image_data, Base64.DEFAULT);
		if (decodedImage != null) {
			largeIcon = BitmapFactory.decodeByteArray(decodedImage
					, 0
					, decodedImage.length);
		}
	}
	return largeIcon;
}

This function requires the following additional imports to be added to the class:

import android.util.Base64;
import android.graphics.BitmapFactory;

Step 3 – Updating the Notification Methods

In this step will update the generateNotification() and generateImageNotification() methods to accept our new large_icon parameter. The first step is to change the function declarations from:

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

To:

private static void generateNotification(Context context
		, String title
		, String message
		, String url
		, String large_icon)

And:

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

To:

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

Now we have to call the setLargeIcon() method passing it the decoded Bitmap in both methods, when the notifications are created. In generateNotification() it will look like this:

Notification notification = new NotificationCompat.Builder(context)
		.setContentTitle(title)
		.setContentText(message)
		.setContentIntent(intent)
		.setSmallIcon(icon)
		.setLargeIcon(decodeImage(large_icon))
		.setWhen(when)
		.setStyle(new NotificationCompat.BigTextStyle()
				.bigText(message))
	.build();

In generateImageNotification() it will look like this:

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

In generateImageNotification() we also need to pass the large_icon parameter to generateNotification() in our failure case:

// If we didn't get the image, we're out of here
if (message_bitmap == null) {
	generateNotification(context
			, title
			, message
			, url
			, large_icon);
	return;
}

The two functions, in their entirety, are as follows:

private static void generateNotification(Context context
		, String title
		, String message
		, String url
		, String large_icon) {
 
	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)
			.setLargeIcon(decodeImage(large_icon))
			.setWhen(when)
			.setStyle(new NotificationCompat.BigTextStyle()
					.bigText(message))
		.build();
 
	notification.flags |= Notification.FLAG_AUTO_CANCEL;
	notificationManager.notify(0, notification);
}
 
private static void generateImageNotification(Context context
		, String title
		, String message
		, String url
		, String image_url
		, String large_icon) {
 
	// 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
				, large_icon);
		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)
			.setLargeIcon(decodeImage(large_icon))
			.setWhen(when)
			.setStyle(new NotificationCompat.BigPictureStyle()
				.bigPicture(message_bitmap))
		.build();
 
	notification.flags |= Notification.FLAG_AUTO_CANCEL;
	notificationManager.notify(0, notification);
}   Intent notificationIntent = null;

Step 4 – Base64 Encoding the Image

The image that we will be sending down to our apps as part of our payload is this fantastic photo of me:

It's me at 49 x 49

There are two sites (I’m sure that there are others as well) that you can use to encode the data: WUtils.com or webcodertools.com. Both produce the same results, but I preferred webcodertools.com because the other added in newlines that I had manually to remove. The only problem with webcodertools.com is that you need to copy the correct text from the output.

<img alt="" src="data:image/jpeg;base64,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" />

We need to copy the text in between

<img alt="" src="data:image/jpeg;base64,

and

" />

So we end up with only the image data as follows:

/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAxADEDAREAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAABgcIBQT/xAAaAQADAQEBAQAAAAAAAAAAAAACBAUDAAEG/9oADAMBAAIQAxAAAAGo+9ihZgUwLtH3gL0i5nn7q/fmS0rq2ZzZ8PBmek20Fejxl/U5avE3lKe3/OHVzm+iji7GD7q7/p1/Iq6/Z52BJlkJxqy9IS7iGs5Fg4zENz8neqoC7LkQ6GVKQdwvomCoAAXKu4g1wVUK1zSbVIwzMZWuBt4tt328z89//8QAJhAAAQQCAgECBwAAAAAAAAAAAwECBAUABhIUEzM0BxEVIiYyNf/aAAgBAQABBQIjuLd3b+VxIT5pD69YhGMUiE4c1Di82FZcKXdRHHsetVDIkRYiOSxhDVkgbq6y+eRPixVFTaZiXl9TSkONJbfJYOTndyGrY/Ww4X7C1Fm+K6vCosbEY8tgjRhmRwRoXHJLeMmKBXupbPyhFIaqXBuI7/vnD1FzrDPKqLXrLq1sMSy70KssZDkAOWwsrpU2B/SN7eP60D2Wz/yQ+pn/xAAnEQACAQMBCAIDAAAAAAAAAAAAAQIQERIDBCEiMTIzQVETYXGBsf/aAAgBAwEBPwFUirmEhZR3ieSpwidxKxeieMqWFSwh8zL7pG3kUUiSEK0U50YjSUtSOSJZLqGzVy5eDcY5SUfZqxk5qzFqRXCZl/LJxlit/v8Ap8yNm70P2bVzNm6Z/kRPpNLtRp//xAAkEQACAQMEAgIDAAAAAAAAAAAAAQIDERIQITEyBCITcTNCgf/aAAgBAgEBPwEqdmN2PkgZIuX0qck3kW0W8dXBu7LG3BLZlNepgIylHgu2QdiRD2tHSO8dLYtxJPE5KSivsuyD9bkMcHdFWk+44ij+qE477GEh/iZS4/qPJ4X0S5PH7kuz0//EADEQAAEDAgIHBQkBAAAAAAAAAAEAAgMREgRBEBMhIjFRcQUzQmFicoGCkZKhsbLBMv/aAAgBAQAGPwJdo+039Gq1jHPoK0ajIcObeQFVrjCW03TVVB93LQ2NuKwW80nbhneXr81ihiHMkm3aujbaDujKqjFu/wAXHmdDmOYDXyT4+DLqDouKwz8QJWYiykgazdBNOB9yg7TwgrgnvjBdUZcU1mxxyc3NGOw7M6oqS4bWmtV3x+p6e0GoBIqtRU6uSRuxE+Bgy5oTEXOtpVFY3tKTa+j7QfkPnokHqQEcWtk/Cj1pebmNJDRnmqxgjq0hOqVHC81wo3mtZ/ea4P8ApV83+G/dNkj3LbnBoYOXBOhxpslJL7neLMq3D1lPMcFLiZzRrRsaoI575Iy6jq9V3f3do+F6wvR37JnRfEPyme1/dH//xAAnEAACAQIFBAIDAQAAAAAAAAABEQAhMUFRYXGRgaHB8OHxECCx0f/aAAgBAQABPyFoZ1E8j09IQPOES1KmiQgAbUrqbUxK5R9IMSZnAkPbKJaRzshER0vENAkJ67fMPTFJYmbPVC/DCxH6U3ORGfUf64YahwZycCoTTI9viE4VyLNb7HTaEVlcILi8MNgJeA+gQfqeyeYATMiJ1gDVaQsC0Tx/BF6LIDMv2jYIkcBM/MzLbQW2V1PkRtI/hiPNYdS1A1RYacykWOqQEQFVYIMRxJUA8wJmESodgT0OrWoNJ9ygURIhQdu0PcYCYAFIZVha4VAep3JhAM7BQ9YJmTI8/MU0Rmi0z2/7S9v4H6VpNr3onf8A8P/aAAwDAQACAAMAAAAQyGlOe8aOnxz0qRPTAfS6zib7/wD/xAAkEQADAAEBCAMBAAAAAAAAAAAAAREhMRBBUWFxkaGxgcHR4f/aAAgBAwEBPxBKyY2NhbgLotiaZj7r8IahSbLWKY0rGmYxkom94uTo3lMkWJDG0HEF2uWUcTFYuD/CClNxGFHgRs9kVRCVYT+P4XiK7k5LXwOeCrcm4Yzqkk+Fme2g06iWbgZi2kdKOr2Z4HoaXRngvvY+s9r3s//EACIRAAICAgEEAwEAAAAAAAAAAAABESExURBhobHwQXGR8f/aAAgBAgEBPxBDUe4Qm7dCZRZCY2T4X7/SauNaFhDClMpMsQnQpqPwhqBSewhUCemNSIDmOwxk1vItwSb6SUPAYVjfQMlShS5aroZ0SB9B8scJZJmr7aHsmayOhDB8eD1o7xGbkB8LPC4//8QAJBABAQACAQMFAQEBAQAAAAAAAREAITFBUWEQcYGRofCxweH/2gAIAQEAAT8QDqk1TNkmg/l39XNp5gz2HgF1fjlMhpMDE5X+fOM5BGWU0+REcmFp+nDnm+jgVBAc1Hu+v4XeHGmcFMDkqXVg/hGzaorrgqeHlVNIUYd39fzG+MBJwNHsD4xj6eGLQ+S/byZOQBuOKmChE32crRbFl4RDe3FNpgSxBo8BlYqW+qywQKpF6Nf4xV7QWczGgzkN2T7Er/30uByHJACBjs4x4q/hpdzugcBllekCatddN9b4ymPNWuAyJ2POsrrQO6/GPIJbRVELp0ZuU0zPN9sRMGLppQ/uMl9tUtgErh3b2x/lkXT4Kanzka+1yBdyb5w8SEBv1mm3CnZADdgPKpd5/Ef8x9Nn11I4mlV2Xilx2qS7s7EL2a645tFbdr2T33ThwssVQDvZH4uUSO6mkKu1ePLxZGENrilUFdOw16O/tYf0e3pd+V/now/ud/p//9k=

Step 5 – Sending the Message

Now we have all of the pieces that we need (the working app and the encoded image) in order to send our message via AirBop. To do this we need to go to our app and send the following custom JSON.

Note: If you are unfamiliar with how to send a Custom JSON message using AirBop please see Step 4 in the previous tutorial.

{
    "title": "AirBop",
    "message": "Mark has sent you a message from AirBop",
    "url": "http://www.airbop.com",
    "large_icon" : "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"
}

When the message gets to your client app it will look like the following:

Conclusion

That’s it for this tutorial. You should now be able to push encoded images down to your app as part of the message payload. The one drawback to this method is the 4K message limit imposed by Google Cloud Messaging. So the images you want to use can only be so large before you run into that limit.

Here are a few other examples I worked on for this post.

Medieval RPG, where your attackers avatar shows up in the notification:

Sports Score app:

With the big picture style as well:

Some sort of strange grind-based RPG:

And we’re done. Now you know another method for adding pictures to your AirBop push notifications. Good luck! Feel free to leave a question or a comment.