Friday, March 25, 2011

Google Analytics Tricks for Android

I previously did a post about how to track version usage with Google Analytics, but I think I've taken that work and created something better.

I'm using the app preferences to determine if a previous version was already installed.  If it's not then the value of appPrefs.getAppVer() will be equal to a null string.  In that case I record an Application Install event with a label of the application version and a value of 1.  That increments our given version label by 1 in Google Analytics.

If our appPrefs.getAppVer() wasn't "" or "N/A" then it was an upgrade so I record an Application Upgrade with a label of the OldVersion->NewVersion and a value of 1.  That increments our given label by 1 in Google Analytics.  To explain, that means I'll get a count of upgrades from 2.9.4->2.9.5 for instance.

In the case that appPrefs.getAppVer() was equal to "N/A" that means it was a new install and the user clicked cancel on the usage agreement the 1st time.  So, we don't want to record anything.

// Display Recent Changes on 1st use of new version
if (!appPrefs.getAppVer().equals(getAppVerName())) {
 if (appPrefs.getAppVer().equals("")) {
         tracker.trackEvent("Application", "Install", getAppVerName(), 1);
      } else if (!appPrefs.getAppVer().equals("N/A")) {
         tracker.trackEvent("Application", "Upgrade", appPrefs.getAppVer().toString()+"->"+getAppVerName(), 1);
 // display recent changes dialog

// Display Usage Agreement on 1st use of new version
if (!appPrefs.getAcceptedUsageAggrement()) {
 // display usage agreement dialog
 // negative button actions here
 // positive button actions here

With these tricks and a couple of others I am recording:

  1. For category "Application" I have the following events:
    1. Run -- What version is being run every time the application starts
    2. Error -- If there was an error I capture the version number that was running
    3. Upgrade -- Capture the old version and the version the user is upgrading to
    4. Install -- Capture the version of a new installation
  2. For category "Groups", "Send Message", and "Files" I have the following events:
    1. Success -- Record when something went well like, the user was able to pick a group that contained contacts, or our SMS message body contained text
    2. Error -- Record when something went poorly like, the user tried to send, but had exceeded the hourly message limit, or the SMS message had no addressees
These events are in addition to regular Page Views and should give me a lot of insight into how people are using my app.  One thing I already see a lot of: Groups -> Error -> No Records Added.  That means the user is successfully picking a group off their phone, but it either has no contacts or no contacts with a valid mobile number.  I'll have to find a way to make them more successful!

Sunday, March 20, 2011

Splitting Text At A Fixed Length

As part of my message app I had originally been splitting my message text "by hand."  I found a method in the SmsManager that does the job for you.  Again, it's still a good bit of code so here it is.  Hope you find it helpful.

This will split the message into 160 character chunks to include anything up to 160 characters at the end of the message.

protected ArrayList<String> splitMsg(SmsMessage smsMessage) {
        ArrayList<String> smt;
        Pattern p = Pattern.compile(".{1,160}");
        Matcher regexMatcher = p.matcher(smsMessage.getMsgBody());
        smt = new ArrayList<String>();
        while (regexMatcher.find()) {
        return smt;

Sending a Single SMS to Multiple Addressees

The net-net-net of this, if you're wondering is, it can't be done.  Hear me out.  I don't want to send 10 SMS messages to 1 addressee.  I can do that just fine.  I was trying to send a single message to 10 addressees in order to get 1,000 addressees the same message rather than Android's 100 addressees/messages.  I learned a valuable lesson about the SDK documentation.  When it says it accepts a "String" it doesn't mean you can trick it to accept a String [].

public void sendDataMessage (String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)

I spent a lot of time yesterday getting the code in place to provide "destinationAddress" as a comma (as well as space, semicolon, and new line) delimited list.  No messages get sent unless your list contains only 1 item.  *sigh*

But, I do like the code I came up with so I'm storing it here in the hopes that it might someday be useful in another context.

protected void sendMsg(Context context, SmsMessage smsMessage) {
        SmsManager smsMgr = SmsManager.getDefault();
        ArrayList<string> smsMessageText = smsMgr.divideMessage(smsMessage.getMsgBody());
        PendingIntent sentPI = PendingIntent.getBroadcast(context, 0, new Intent("SMS_SENT"), 0);
        PendingIntent deliveredPI = PendingIntent.getBroadcast(context, 0, new Intent("SMS_DELIVERED"), 0);
        int AddresseesPerMessage = 10;
        StringBuilder builder = new StringBuilder();
        String delim = "";
        for (ContactItem c:smsMessage.getAddresseeList()) {
            //  For every phone number in our list
            if (((smsMessage.getAddresseeList().indexOf(c)+1) % AddresseesPerMessage) == 0 || smsMessage.getAddresseeList().indexOf(c)+1 == smsMessage.getAddresseeList().size()) {
                // using +1 because index 0 mod 9 == 0 
                for(String text : smsMessageText){
                    //  Send 160 bytes of the total message until all parts are sent
                    smsMgr.sendTextMessage(builder.toString(), null, text, sentPI, deliveredPI);

Wednesday, March 16, 2011

Broadcast SMS Lite v2.9 Released (Feature Complete)

After just 6 short weeks I am proud to announce the Feature Complete version of Broadcast SMS Lite v2.9!

Broadcast SMS is not intended to replace your handset's SMS messaging tool.  It's rooted in direct marketing.  It was built to talk a sales leads file and broadcast a SMS message to all the phone numbers contained within.  That part was working from version 1.0, but it did get me thinking: I want to be able to send a message to a group of people as quickly as I can.  So, I leveraged the basic idea for Broadcast SMS and created just that!

I send a lot of group texts: to my kids, to friends, etc.  I always hate composing them because I had to select the individuals or get my messaging app into the right place to do so.  Broadcast SMS does this by default. In fact, I will not be building a way to add a single contact to the list of addressees.  Of course I can be convinced otherwise, but that's my going in position.

When you launch the application you're presented with the status screen.  Remember, it's rooted in direct marketing ;)  From there you only have 2 selections: 1 lets you read an input file and the other lets you choose from your predefined contact groups.  You're immediately presented with the message window and a Send (Broadcast) Message button.  That's almost as few clicks as you can make to get a message out to your friends, family, etc.

As always, bugs and feature requests equally appreciated to

Monday, March 14, 2011

Broadcast SMS v2.8 Released

A simple status screen has been implemented.  It will show you how many messages you can send in the next 60 minutes without hitting Android's 100 messages per application per hour limit.

Saturday, March 12, 2011

Broadcast SMS v2.7 Released

It's finally ready! v2.7 introduces a new tabbed UI. Many more under the covers (refactoring) updates, and some other minor tweaks.

Wednesday, March 9, 2011

What's new with Broadcast SMS?

You may be wondering why the next update is taking so long. It's a lot of work. That's why ;) Here's a sneak peak:

Sunday, March 6, 2011

Android: Working with Sqlite Cursors & Queries

If you're like me you learn to code by example.  One of the things I was having trouble with was selecting data from sqlite.  Not that I couldn't figure out how to get all the data, but how could I get just the data I needed.

The generic getContentResolver.query(Groups.CONTENT_URI, null, null, null, null); just seemed wasteful.  Here's what I've gleaned from other posts around the internet and from the SDK documentation:

ContentResolver cr = getContentResolver();
        Cursor groupCur = cr.query(
                Groups.CONTENT_URI, // what table/content
                new String [] {Groups._ID, Groups.NAME},    // what columns
                "Groups.NAME NOT LIKE + 'System Group:%'", // where clause(s)
                null, // ???
                Groups.NAME + " ASC" // sort order

Further, if you want to just get a single row by it's _ID you can do this:

ContentResolver cr = getContentResolver();
        Uri myGroup = Uri.withAppendedPath(Groups.CONTENT_URI, "16");
        Cursor groupCur = cr.query(myGroup, new String [] {Groups._ID, Groups.NAME}, null, null, null);

Hope this is helpful and saves you some grief ;)

Thursday, March 3, 2011

Android: Tracking Version Usage

I had wondered and I've heard it asked, "How do I know what versions of my Android application people are using?"  The quick answer that you'll get is, "You don't."  I found a way using Google Analytics to track a couple of things: 1) you can track the number of people upgrading and/or 2) you can track the specific version usage!

I created a pop-up dialog that announces the recent changes to the user after an upgrade.  Inside that logic I put a trackEvent call to Google Analytics with a label of the Application Version and a value of 1 so each time that dialog is presented I get a +1 for that label.  Here's how:

if (!appPrefs.getAppVer().equals(getAppVerName())) {


    tracker.trackEvent("Upgrade", "Click", getAppVerName(), 1);


getAppVerName() is just a helper method that does the following:

public String getAppVerName() {
    String text;
    try {
        text = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
    } catch (NameNotFoundException e) {
        text = "Version Not Found";
    return text;

On your application's main activity screen you can also add a trackEvent to help keep track of which versions are being executed like so:

public class MyActivity extends Activity {

    GoogleAnalyticsTracker tracker;

    public void onCreate(Bundle savedInstanceState) {
        tracker = GoogleAnalyticsTracker.getInstance();
        tracker.start("UA-12345678-1", this);
        tracker.trackEvent("HomeScreen", "Click", getAppVerName(), 1);

Google Analytics will record 2 events for you: one called Upgrade and the other called HomeScreen. Both will have a label of the version making the call and a counter.  Also of note; in the recent changes logic I also set the Accepted Usage Agreement to false allowing me to re-display the usage agreement on upgrade.

Tuesday, March 1, 2011

My First Troll!

I, or my App rather, have/has arrived!

by rudo (March 1, 2011)
It sucks, does not work at all and freezes m phone
I figure if I'm not doing something right I wouldn't have hate mail.  Internet trolls are an odd bunch.  The Android Developer Console doesn't report any Force Close or Freeze issues so my best guess is a competitor not liking how well my app is doing :)
1,400+ downloads with ~400 active installs totaling more than 13,000 activity views since February 12.  There hasn't been a reported error from the app or from the Android Developer Console in over a week (since version 2.2.3).
The best feature to date is very close.  If you text groups of people frequently and are, or are not a direct marketer you're going to really like the next update.