beta
Hello developer. Login with your existing account. New to Vodafone Developer? Register your account.

+ Login or create an account

Remixing apps with Android intents

Introduction

Intents are one of Android's "Big Ideas". From the beginning, Android was conceived as a "mobile mashup platform". We commonly think of mashups in a web context, as a "remix" UI displaying data from multiple services within a page. With apps, and Android, the opportunity is a little bit different, but the idea is the same – to enable developers to aggregate and build on the work of others, and to "stand on the shoulders of giants" when creating new apps.

In Android, "remixing" other apps to build your own app means re-using the functionality and UIs of other apps as components within your own. For example, instead of writing your own file browser, re-use as much (or as little) of someone else's File Manager app as you need; instead of writing your own print-from-app functions, re-use someone else's; instead of having to write your own Twitter interface, re-use a dedicated Twitter app; instead of writing your own image-editing functions, re-use Photoshop's.

There are obvious advantages: as Andy Rubin (one of Android's founding fathers) puts it, "more flexibility for the developers, less work, faster turnaround, rapid prototyping..." There are some less obvious advantages too: re-using functionality from built-in apps instead of rolling your own leads to a greater consistency between apps, which users like, and to a recognisable platform feel. It also encourages a task-based, "collaborating components" approach to app design, which fits mobile use cases well, and is also lightweight. Best of all, you can collaborate with other third-party apps too, not just built-in apps.

And just in case you're wondering – Android doesn't just make this style of programming possible, it makes it easy.

The rest of this article provides a quick introduction to using intents in Android, with example screen shots of some apps and components that are ready for re-use, and pointers to resources that will help you explore for yourself.

Android intents

If you've done any Android programming, you'll know that Android does things a bit differently. Two concepts that are at the heart of an Android app are activities and intents:

  • Activities are the "units" of an Android app; an activity typically encapsulates a single app screen and its associated functionality. Every app must define at least one activity, otherwise it's not an app (though it could be a service i.e. a background task)
  • Intents are declarative action requests that can be sent between activities (declarative in that they tell "what", not "how")

Intents are part of the basic Android application architecture. They provide a general purpose brokering service that matches requester activities with provider activities (for the purposes of this discussion, we will ignore broadcast intents, which are more like notifications). Intents enable developers to wire together apps from separate program parts, including parts provided by others.

In some ways, of course, that's not new; it's what client-server does, and what frameworks do. But unlike servers or frameworks, providers are not privileged components; requesters and providers are equals, and both can be third-party apps. Also unlike servers or frameworks, intent action providers are complete activities that include their UI, so when you get service from a provider you get the UI too.

Android is not the first platform to enable this kind of functionality. For example, Symbian allows app embedding, which is basically the same idea. The difference is that Android generalises the mechanism and makes it completely open. It also makes it easy; not just for requesters, but for providers too.

Extending your app with intents

To see how it works, the following code is a simple template for an activity that launches an action from a button press. Adapted from the main activity class generated by the Eclipse New Project wizard, it overrides the default activity onCreate() method and adds a new Button object, on which it sets OnClickListener() and onClick() methods. To complete the example, you'll need to add appropriate text strings to res/values/strings.xml (not shown), and add button layout code to res/layout/main.xml, for example:

<Button
  android:id="@+id/button1"
  android:layout_width="400px" 
  android:layout_height="wrap_content" 
  android:text="@string/presstosend"
  android:layout_centerHorizontal="true"
  android:layout_below="@id/text"
/>

The complete activity class code becomes:

public class mashtest extends Activity
  {
    /** Called when the activity is first created. */
    @Override public void onCreate(Bundle savedInstanceState)
      {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        /*
         * Do something when the user presses the button.
         */
	((Button) findViewById(R.id.button1)).setOnClickListener(
	  new Button.OnClickListener()
            {
              public void onClick(View v) 
                {
                  /*
                   * Test code goes here - it will run when the
                   * button is pressed.
                   */
		   // ...
                }
            });
      }
  }

Send an SMS message

To start with, let's look at something simple. It's easy to send an SMS message from an app: just call SmsManager.sendTextMessage() with two strings:

  • The destination phone number
  • The message text to send

Drop the following code into the onClick() method template. Note that we do not instantiate SmsManager (it's a static object), we just request a reference to it. If you provide a real phone number, build, and run this on your Android phone, it will send an SMS message when you press the button.

String num = "12345678"; // Replace with phone number
String msg = "Hello!";
SmsManager sman = SmsManager.getDefault();
sman.sendTextMessage(num, null, msg, null, null);

You'll also need to add an import statement at the top of the class file:

// For the SMS Manager
import android.telephony.gsm.SmsManager;

And you'll need to add an appropriate permission to the app Manifest.xml:

<uses-permission android:name="android.permission.SEND_SMS">
    </uses-permission>

The code is minimal, and you can certainly do better: it performs no error checking, and provides no user feedback or acknowledgement. It does send an SMS, though.

Send an SMS message using intents

Using the code above, your app uses the messaging framework (i.e. the SmsManager) directly. An alternative approach is to use an intent. Instead of making an API call, you declare an action request, and let the intent framework find the provider.

In fact, there are several different ways to do this. When you use intents, they can be explicit or implicit:

  • Explicit: the requesting app specifies which activity should perform the requested action
  • Implicit: only the action but not the activity is requested

In the first case, the requester provides a package specification, and the system resolves the request to that package only (if it is present on the phone at runtime).

In the second case, the intents framework will attempt to resolve the request to any suitable provider activity. This may require the user to make a selection if more than one provider is found.

To launch an activity using an intent:

  • Create a new Intent object, supplying an action description and a URI on which the action is to be performed
  • Pass the intent to the startActivity() static framework method
  • Optionally, you can specify a provider package, qualify the intent category and data type, and supply additional data, using the Intent methods putExtra(), setType(), setData(), and so on. See the android reference documentation for details.

Note that using intents does not require any changes to the app's Manifest.xml file.

Here are two versions of an intent to send a message from an app: in both cases, instead of getting a reference to SmsManager, the code simply launches the messaging activity using an intent. Note that the request is implicit; we don't specify which activity should be launched, instead it is inferred from the URI provided.

In the first example, because no number or message is supplied, there are no details filled in when the messaging activity launches:

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse( "sms:"));
startActivity(intent);

In the second example, the activity launches with the number and message filled in:

Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse( "sms:" + num ));
startActivity(intent);

The screen shots below show the app screen with launch button, and the results of the examples above:

By the way, if you try the second version without supplying the Uri argument, you'll see an error screen when you try to launch the intent, because the action syntax requires a URI; whereas, if you try the first version without the Uri argument, the user is presented with a list of apps to choose from. The difference is the difference between the action syntax for ACTION_VIEW and ACTION_SENDTO.

More about intents

By definition, the intents framework is asynchronous. The intent itself is just a data structure: it's the startActivity() call that does the work. Because it is asynchronous, to get data back from the activity that is launched to provide the action you requested, you must implement a call-back method.

To have data returned, use the startActivityForResult() method, and override the onActivityResult() callback in your main activity code to receive the data. Both methods include an int parameter you can use to match results to requests. Again, the Android documentation is comprehensive and includes example code.

It's also important to remember that intents are a late-binding mechanism i.e. requesters and providers are bound at runtime. You code will always build, but if there is no provider for the action you request when your code runs on a given device, it will not run as you expect. Your code should therefore check for availability of a provider and be rigorous in handling errors, and catching possible exceptions – not shown in the code examples here.

Third party intents

Android itself defines many standard Activity action intents, for which there are system or built-in providers, and they are well documented. Some of the more obvious actions include:

  • ACTION_CALL activity – Initiate a phone call.
  • ACTION_EDIT activity – Display data for the user to edit.
  • ACTION_MAIN activity – Start up as the initial activity of a task, with no data input and no returned output.
  • ACTION_SYNC activity – Synchronize data on a server with data on the mobile device.

Vendors including Ericsson Labs and Google also provide extension activities for a variety of services, including mapping.

But the real potential of intents is that any app, which means any third party app, including yours, can declare itself a provider just by including an appropriate intent filter in its Manifest.xml file. A filter describes a capability that is supported by an activity; in effect it's a mechanism that allows you to define a declarative API through which other apps can re-use your app functionality.

A few developers have warmed to this idea, and you can find the beginnings of an intents market emerging on the web, for example at OpenIntents.

The following examples show the code used to invoke a variety of apps and components that publish their activities and that can be used by other third-party apps. The screen shots show the results in each case. The following is just a sampling of course; check out the full list at the link above. And of course, you may decide to add your own app activity to that list.

OI File Manager

OpenIntents develops several free apps for Android, including the OI File Manager. It's not just a must-have app for your phone, it's a ready made file system browser that publishes activities that you can re-use in your own apps. It is documented here. You can use it from your own app to select, open, and save files.

This example opens a directory for browsing, with text prompts to the user. Note that this code only shows how to launch the intent, but you can find a complete code example from the link above that demonstrates how to receive and handle returned data.

/*
 * Pick folder android.intent.action.PICK
 * Pick a folder through a file manager.
 * android.intent.extra.TITLE could be used as well.
 */
Intent intent = new Intent("org.openintents.action.PICK_DIRECTORY");
intent.setData(Uri.parse("file:///sdcard"));
intent.putExtra("org.openintents.extra.TITLE", "Please select a folder");
intent.putExtra("org.openintents.extra.BUTTON_TEXT", "Use this folder");
startActivityForResult(intent, 1); 

ZXing barcode scanner

Zebra Crossing, or ZXing, publishes the ZXing barcode library. It's an open-source image processing library implemented in Java, with support for an impressive range of 1D and 2D barcode formats. It is documented here. You can use it to add full barcode reading capabilities to your app.

This example launches a bar code scanner. As with the File Manager example, we expect data to be returned and so need to create a handler, as well as launching the intent.

Intent intent = new Intent("com.google.zxing.client.android.SCAN");
/*
 * public Intent setPackage (String packageName)
 * Since: API Level 4
 * (Usually optional) Set an explicit application
 * package name that limits the components this Intent will
 * resolve to. If left to the default value of null, all
 * components in all applications will considered. If
 * non-null, the Intent can only match the components in the
 * given application package.
 */
intent.setPackage("com.google.zxing.client.android");
intent.putExtra("MODE", "SCAN_MODE");
// Request the scan
startActivityForResult(intent, 0);

Photoshop Express

Photoshop Express is a free image editing app for Android published by Adobe. You can re-use Photoshop functionality in your own app using intents. You can find complete example code on the Adobe developer site.

Note that the Photoshop use case expects the user to select the image using a gallery app, which returns a content URI, not a file URI. In fact, Photoshop Express won't read a file URI, and although it will launch if passed one, no file is loaded. You will need to work around this if you want to select the image programmatically based on its file name.

Uri imageToEditUri = Uri.parse("content://media/external/images/media/16" );
// Must be of type "image/*"
//String imageToEditMimeType = "image/*";
String imageToEditMimeType = "image/jpeg";
Intent launchEditor = new Intent();
launchEditor.setAction(Intent.ACTION_EDIT);
launchEditor.setDataAndType(imageToEditUri, imageToEditMimeType);
startActivityForResult(launchEditor, 0);

Open Table restaurant booking

As well as adding functionality like file browsing or bar-code scanning to your app, there are services that publish activities. For example, the OpenTable restaurant booking service is re-usable from third-party apps. You can read more about the API and how to use it here.

This simple example launches Open Table. Unlike the previous examples which use Intent methods, like Intent.putExtra(), to set options for the requested activity, OpenTable expects you to put this data into the URI itself. Also, to use the API effectively, you'll need to know how Open Table identifies venues. Even so, it's a good example of how to make a service open and reusable.

startActivity(new Intent("com.opentable.action.RESERVE",
  Uri.parse("reserve://opentable.com/%s?")));

Open Table shows a splash screen when it launches, followed (in this example) by a booking screen.


ColorDict

The last example is a third-party dictionary app. This example is interesting, because it launches as an overlay over your own app screen. The exact result depends on the user settings for the ColorDict app. In this case, Chinese and French dictionary and Wikipedia search results are displayed for the queried term. You can read more about ColorDict here.

As you can see from the example, the intents mechanism allows complete flexibility in defining an API for the activity you publish, including fine-tuning your UI appearance.

Intent intent = new Intent("colordict.intent.action.SEARCH");
intent.putExtra("EXTRA_QUERY", "hello"); //Search Query
intent.putExtra("EXTRA_FULLSCREEN", false); //
//400pixel, if you don't specify, "fill_parent"
intent.putExtra("EXTRA_HEIGHT", "fill_parent" /*400*/); 
intent.putExtra("EXTRA_GRAVITY", Gravity.BOTTOM);
intent.putExtra("EXTRA_MARGIN_LEFT", 100);
startActivity(intent);

Summary

Intents are such a good idea, it's surprising that they haven't been taken up more widely as a way of opening up component functionality. One issue, of course, is what happens if an activity you use is not present on the user's phone when your app runs? There are several solutions: you might choose to make some user choices unavailable, for example with greyed-out menu items. Alternatively, you could take the user to a store to download the required app.

And of course if this is your app, what better way to drive downloads than to make some cool functionality available through it, and to promote that functionality to other developers.

Find out more

The "mobile mashup platform" description is from Andy Rubin, one of Android's founding fathers. The quote is from this Android launch interview.

Application Fundamentals is a good place to start with the Android online documentation, and includes a good overview of intents.

For a detailed discussion of intents and intent filters, see this guide.

For a full list of the standard Android activity action intents, see this reference.

OpenIntents is an excellent resource for developers wanting to re-use or publish app functionality via intents.