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()) {
            smt.add(regexMatcher.group());
        }
        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
            builder.append(delim).append(c.getPhoneNumber().toString());
            delim=";";
            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);
                }
                builder.setLength(0);
                delim="";
            }
        }
    }

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);
    appPrefs.saveAppVer(getAppVerName());
    appPrefs.saveAcceptedUsageAggrement(false);

 }

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;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        tracker = GoogleAnalyticsTracker.getInstance();
        tracker.start("UA-12345678-1", this);
        tracker.trackPageView("/HomeScreen");
        tracker.trackEvent("HomeScreen", "Click", getAppVerName(), 1);
        tracker.dispatch();
        setupViews();
    }
}

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!


1-star
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.