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

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:

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

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.

Sneak Peek: New Dashboard None Style

A few weeks ago I posted a sneak peek look at the New Dashboard’s List Style before that there was the Gallery style. This week I want to take a look at the new None style.

Let’s say you only wanted to have one activity within your app (I wouldn’t recommend it but there are reasons for doing it) in the old classic dashboard when you users started your app they would be greeted with the following:

Classic dashboard with one activity

Not that useful, fortunately we’ve addressed this with new Dashboard’s None style:

None Style

The None style is what you’d expect given the name…no dashboard, none whatsoever.

Here is what the same application would look like using the new None style:

Just the activity, no dashboard here

It’s just the activity. Instead of a dashboard your users will be sent straight to an activity that you specified. You can think of this default activity as your “home” activity.

The None style will of course support multiple activities:

Mutiple activities are still supported

In this type of an app the “home” button in the action bar will take you back to the activity that you have specified as your “home” activity.

While the None style may not be the most exciting of the new styles to detail in a blog post, it does provide you with the ability to create something in Andromo that was impossible before and will make certain Andromo applications much better.

That’s three new dashboard styles down and three more styles to go. See you next time!

Sneak Peek: New Dashboard List Style

Last week I posted a sneak peek look at the New Dashboard’s Gallery Style. This week I want to take a look at the new List style.

Note: As I mentioned in the last post, what you are seeing here is still being worked on, so the end product will likely be different then what you see here.

I’ll start off by showing you what the new dashboard I designed for my fictional app looks like with the exact same settings using the current “classic” dashboard style:

Still a pretty nice look

Now let’s see what we can create using the new Dashboard’s List Style:

List Style

If you’ve used any Android app’s you should be pretty familiar with lists. Lists are one of the core user interface elements on the Android operating system, and there’s a reason for that: they work well. One of the great features of lists is that they give you ample room to display extra information. As a result we’ve introduce the idea of “sub-text” or “subtitles” to the activities. This will let you, the Andromo developer, provide your users with two lines of optional text per activity:

The List style showing subtext

As you can see I’ve added a blue pattern to the new logo area (discussed in the previous post) and some sub-text providing some information about the activities.

Here is the same dashboard in landscape:

The List style in landscape

As you can see I’ve turned off the logo area in portrait mode. When you start developing with the new dashboard you’ll be able to do the same thing with a checkbox to enable or disable the logo area in whichever orientation you want.

Like the Gallery style, the list style accepts an almost infinite number of activities and it is (of course) scrollable. In portrait mode the list will scroll underneath the fixed logo area.

Icons are also optional and can be taken off of the dashboard without having to edit any activities:

Look Ma no icons

The new Dashboard also lets you change the way in which the panel area (the list for this dashboard style) looks, giving you another way to change the look of your dashboard:

Here the panel is "floating"

Note: There is a visible bug in the above screen shot, don’t worry we’ll get to it. Like I said, we’re still working on this.

It’s important to note, that the settings are (for the most part) the same between multiple new Dashboard styles. This means that you will be able to test different styles without having to change or configure a lot of different options.

Here’s one last look at the new Dashboard list style in portrait mode, but with the logo area turned off:

The new Dashboard is much more configurable

Well that’s it for the sneak peek look at the new Dashboard List Style, only four more dashboard styles to go!

Note: The observant among you might notice something strange in the top left corner of the new Dashboard screen shots…more on that in a future blog post.

Friday Sneak Peek: The Photo Gallery Activity

Time for another sneak peek behind the Andromo development curtain, today’s glimpse will be another new activity. We’re going to take a quick look at the new Photo Gallery activity. As with all sneak peeks you have to remember that this is not the finished product so the name, functionality, look-and-feel, etc. might all change before the activity has been released. That being said let’s take a look at this new activity.

The Photo Gallery was designed to let Andromo developers bundle a static set of images in their Android apps. Right now there is no limit to the number of photos allowed, but the standard size limit of 50Mb is still in place.

Some of the features we are planning on including are a gallery index, optional caption information for each photo, and the ability for the end users to pinch-zoom the photos to get a closer look:

We also wanted to take advantage of some of the modern User Experience (UX) patterns that have become popular in the Android ecosystem so we implemented the swipe gesture for navigating between photos:

Finally we’ve let the developer enable or disable the options to share the photos or set the photos as the phones wallpaper:

We’re still working away on this new activity so I can’t give any details as to when it will be release, but I will say that we’re hoping it will be soon. (It’s always soon isn’t it?)

That’s it for this quick little Friday sneak peek at the Photo Gallery app, I’ll leave you now with my donkey eye wallpaper:

The Audio Activity – A Quick History

When we create new activities for Andromo they often start one way and end up another. They generally change for two reasons:

  1. Feasibility – Something gets in our way and we cannot accomplish what we set out to do. This will often be a limitation in Android or the hardware, but it can also be other considerations like security or apk size.
  2. Usability – It just doesn’t feel right. We get the activity going, and while it’s a great idea on paper, when you get it on your phone something’s wrong.

Our more recent activity, the Audio activity, was no exception to this pattern. Here is a very early iteration of it from October 20th 2011:

Pre-Release Version of the Audio Activity

Here is the Audio activity that we ended up releasing on December 21, 2011:

Released version of the Audio Activity

As you can see in those two months the Audio activity changed a lot. We put a lot of time and effort into the improving the user interface and the user experience. This process involved a lot of meetings, experiments, and playing with other applications, before we signed off on the final design. There were, of course, intermediate interfaces that we played around with, but sadly I couldn’t find any screen shots of them.

In the original version of the Audio activity we put a lot of emphasis on the album artwork and description, but after playing with it for a while we decided that it took too much away from the most important part of the Audio activity: the audio tracks. The downside was the removal of the album description from the Audio activity, but we thought that some of our other activities (Custom Page, HTML Archive, etc) were actually better suited to the task.

The final design puts the audio tracks front and center and still allows you see the album artwork without robbing your of that precious screen real estate. It has also looks a lot cooler. In the end we simplified the user interface, and even though we removed some options (perhaps because we did) we ended up with a better solution. Now show me another app maker with an activity like this…

Arnie also likes the soundboard mode

Does Andromo Work?


Short Answer:

Yes.

Long Answer:

Yes, and let me explain. First a few numbers, as of today, Andromo has over 12,500 confirmed users signed up, and that number has been doubling each and every month since Andromo was released. Would we be growing like that if we had a product that didn’t work?

Still not convinced? I understand people make a lot of claims on the Internet, why not take a look at all of the App that have been created by Andromo and uploaded to the Android market. If you follow that link you will see a list of all of the Andromo apps that are currently in the Android Market.

Andromo Apps in the Android Market

Andromo Apps in the Android Market

Now we don’t think that there are one thousand apps in the market just yet, but maybe Google knows something that we don’t or they’ve seen those numbers grow and they’re just preparing the market.

Need more proof? Why not see for yourself and install a few of these examples of successful apps that were made with Andromo:

Tablet Market, has between 100,000 and 500,000 downloads of the free version and between 100 and 500 installs of the paid version.

Tablet Market

Tablet Market


How to Apply Makeup
was added to the Android Market on December 23, 2011 and as of today, January 10, 2012 it has between 10,000 and 50,000 installs. Edit: As of Thursday January 12, 2012 the installs have jumped to between 50,000 and 100,000.

How to Apply Makeup

How to Apply Makeup dashboard

Calm Baby a new Andromo app on the market with 100 to 500 installs and making use of our newest activity the Audio Activity.

Calm Baby uses the Audio Activity

Calm Baby uses the Audio Activity

Minecraft News Center made by Andromo developer Lorne with between 1,000 to 5,000 installs.

Mincraft News Center showing some YouTube videos

If you are still wondering if Andromo works or not honestly download one of those apps, or one of the hundreds of others that are available in the Market and see for yourself. Once you are convinced head over to andromo.com and start making your own Android App, you’ll soon see that Andromo does indeed work.

An App in the Market or: The Story of a Puck Head – Part Two

Part two

Continued from Part One.

A few more statistics and thoughts about Puck Head, an Android app made with Andromo that I uploaded to the Android Market. All part of my attempts to understand what makes an Android app popular, and what app makers can do to get more people to use their apps.

Tuesday, November 29, 2011

Well at this point the Puck Head has been out for almost one week, and the downloads seemed to have stagnated at nine active installs, which isn’t that impressive yet. On the plus side according to my AdMob statistics someone is actually using the application. That makes me happy hopefully there is someone out there that actually likes and hopefully uses the app.

Another plus is that I’ve actually got two people to rate the application. One person gave it 5 stars and the other person gave it two. I’m not concerned about the ratings themselves, what does please me is the fact that people have actually taken the time to rate the app.

So far I have done the following to promote this app:

  1. I tweeted about it: tweet
  2. I blogged about it in: part one of this series
  3. I posted a note about the blog post on google+
  4. I tweeted about the blog post: tweet
  5. I started a thread about the app on the Andromo forums: thread

Really this isn’t that much in the form of promotion, but it does get the link out there to a handful of the people that follow me, or pay attention to what I write.

Thursday, December 8, 2011

We released Andromo 1.5 on December 8th with the Custom Page action and numerous bug fixes so I decided to upload a new version of Puck Head. I didn’t add any new activities to the app, I simply built with the new version of Andromo to give my users access to the fixes once they updated the app.

Friday, December 9, 2011

I tweeted another link to the app in the market
Tweeted about it again: tweet and posted a quick note on google+. I’ve been too busy working on Andromo to do much more!

Monday, December 12, 2011

So the app has been in the market for about 19 days, so let’s see where are are:

So far Puck Head has been able to get 59 people to install it in total and 32 that actively have it installed on their device. Not terrible, but not really that great either. Looking at everything that I’ve done (and it really hasn’t been that much) the one event that seemed to have the most impact was when I published version 1.0.0.1. If you look at the above screen shot you can see that the biggest spike was when I updated Puck Head to 1.0.0.1 in the market on December 8th. That got me an additional 7 users, which doesn’t sound like that much but accounts for about 22% of my user base.

What I’ve Learned So Far

I don’t know if I’ve learned anything so far, it’s been too soon, and the numbers are too small. But I have two theories so far:

  • People notice when you update you app: I think it has something to do with some of the app scraping websites out there, but when you release an update it seems to attract people.
  • Screen shots help: Putting a little bit of time an effort into the screenshots you put in your Android Market listing. When people see screenshots I think that they trust the app a little bit more than those apps without screenshots. I know it’s a bit of work, but we have a tutorial for that. Plus if I’m right it will help you get a few more users.

To be continued when I get some more results…