Thursday, August 19, 2010

Sending Emails on Android

UPDATE 2/25/2011: There is a much better solution to the problem I have now posted.

In this post I outline how to share information solely via email. There's some background information up front; if you're interested in just how to share data only with email apps, skip to the bottom.

One thing that users like to do a lot is share information. They want to be able to post things to Twitter, Facebook, or email their friends. Android makes this really simple via the ACTION_SEND intent:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_SUBJECT, "A Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Here's my message.");
Intent mailer = Intent.createChooser(intent, null);

There's only one issue that arises from this method: any app that's registered to receive the ACTION_SEND intent will show up in the chooser. This means some oddball choices pop up ("Bluetooth" being the weirdest one for me), but that's kind of unavoidable. The bigger issue that I ran into was a matter of message length; Twitter messages are inherently shorter than what I might send via email. If I knew I only had 140 characters, the share message should be a lot shorter; but with the freedom of email, I can template out detailed information.

One solution that I've used is to pop up a dialog which asks the user whether they want a short template message or a long one, with examples of where to use each - short (Twitter, Facebook) vs. long (email).

A bigger problem arises when you want to only share information via emails. Suppose the information your app shares is just too complex for a short Twitter message - an unfortunate circumstance for the Twitter population but a fact of some apps. How do you force the chooser to select email? It turns out that there's a very roundabout way of handling this issue by taking advantage of the mailto protocol in Android:

Intent intent = new Intent(Intent.ACTION_VIEW);
Uri data = Uri.parse("mailto:?subject=" + subject + "&body=" + body);

Using the data scheme that you do, only email apps pick up on this scheme. As a result, only email apps will open. You may not even force the user to go through a chooser if they have a default email app chosen already.

Wednesday, August 11, 2010

Palm and CRUD

I've been working with some Calendar integration on Palm and it's a mess because their documentation is out of line with reality. The basic CRUD API I have some issues with because of their ill-defined error codes - for example, if I want to find out if I've already created a calendar event or not I use service getEvent, but the error it returns if the event doesn't exist is the same error as if you input an invalid parameter. But beyond that there's simply mis-documented code or bad samples they give out that don't work.

Some things I've found:

createAccount - There's a lot of different hogwash in the documentation and samples, all of it wrong in one form or another. The icons parameter should actually be setup thus:

icons: {
"32x32": Mojo.appPath + "icon32.png",
"48x48": Mojo.appPath + "icon48.png"

Mojo.appPath is required, and then after that you link your icons from wherever they are in your project.

Accounts Linked to Email - There was some initial confusion because when I created an account with domain of "idunnolol" with username "dlew", Palm would give me the option to log into an email account "". The fact of the matter is, Palm always thinks the account you create is linked to an email, even if you're just using for email. Sorry! You'll just have to get over this.

updateEvent - The documentation would have you believe that it takes two parameters - event and trackChanges. But when you do that, you get the error "ErrorGenericInvalidParameter: Missing Required Parameter: event.startTimestamp". What you really need to do is setup your event as the entire parameters. I have no idea where trackChanges could go (but I don't use it):

parameters: {
eventId: '1241251801',
startTimestamp: '92912911212',
endTimestamp: '1299592929291',
... etc

Besides those three things, I found that the code was much easier to work with if I setup a chain of onSuccess(), onFailure() calls that always guaranteed that I had a valid account and calendar. When I try to create a calendar event, first it checks that I have an account, then it checks if I have a calendar, and creates them if it doesn't exist. It's nice to have this built into your implementation because users can (at any time) delete your account if they feel like it.