Wednesday, July 8, 2009

Component Architecture, Android Style

Android has a nifty component framework, where each screen - called "activities" should be self-contain can be called up by anybody through an event distribution mechanism called "intents". Activities can also register to handle arbitrary intents, which allows applications to delete certain functionality without even knowing which app is going to provide this functionality. Certain intents are pre-defined - e.g. the NoiseAlert app uses the ACTION_CALL system intent to delegate the making of a phone call to the dialer app typically, or whoever can handle it. Anybody can define new intents, but to pull off useful delegation of functionality between completely unrelated apps is usually a bit harder to pull off.

But here is an example of that. A while ago, a user of the BistroMath tip calculator was asking whether I could add the functionality of recording and tracking dining expenses over time. I can see this would be a very useful functionality. But I also think that mobile apps should follow the Unix philosophy, where each program does one thing well and interconnect with others to provide more complex functionality.

Basically I didn't want to build expense tracking into BistroMath, but wouldn't mind interfacing with a specialized application through the intent framework. So I was checking out expense tracking apps on the market (there are quite a few) and emailed the authors of a few which I liked and thought would work reasonably well for what the user had requested.

Within 24h the author of Funky Expenses had replied with a proposed intent interface and a new version of his app which implemented it. I just added a little code to BistroMath to trigger it and there we have a tip calculator with expense tracking capabilities.

For anybody who wants to support the same intent in their expense tracking application or to support calling it from tip calculators or other financial apps, here is an example of the caller interface:
Intent launchIntent = new Intent();
launchIntent.setAction("com.funkyandroid.action.NEW_TRANSACTION");
launchIntent.putExtra("com.funkyandroid.DATE", System.currentTimeMillis());
launchIntent.putExtra("com.funkyandroid.PAYEE", "Per Se");
launchIntent.putExtra("com.funkyandroid.CATEGORY", "dining");
launchIntent.putExtra("com.funkyandroid.AMOUNT", "1532.42");
try {
startActivity(launchIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "No application found to handle expense reporting functionality", Toast.LENGTH_LONG).show();
}
Any of the extra attributes can be omitted and should then appear blank or as default values in the input mask of the event tracking application which is called up. The intent is also documented at the OpenIntents intent registry.