Android offers a very powerful and yet easy to use tool called intents. An intent can be use to turn applications into high-level libraries and make code re-use something even better than before. The Android Home screen and AnyCut use intents extensively to create shortcuts for instance. While it is nice to be able to make use of a loosely coupled API, there is no guarantee that the intent you send will be received by another application. This happens in particular with 3rd party apps, like Panoramio and its RADAR intent.
While working on a new application, I came up with a very simple way to find out whether the system contains any application capable of responding to the intent you want to use. I implemented this technique in my application to gray out the menu item that the user would normally click to trigger the intent. The code is pretty simple and easy to follow:
/**
* Indicates whether the specified action can be used as an intent. This
* method queries the package manager for installed packages that can
* respond to an intent with the specified action. If no suitable package is
* found, this method returns false.
*
* @param context The application's environment.
* @param action The Intent action to check for availability.
*
* @return True if an Intent with the specified action can be sent and
* responded to, false otherwise.
*/
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
Here is how I use it:
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
final boolean scanAvailable = isIntentAvailable(this,
"com.google.zxing.client.android.SCAN");
MenuItem item;
item = menu.findItem(R.id.menu_item_add);
item.setEnabled(scanAvailable);
return super.onPrepareOptionsMenu(menu);
}
In this example, the menu is grayed out if the Barcode Scanner application is not installed. Another, simpler, way to do this is to catch the ActivityNotFoundException when calling startActivity() but it only lets you react to the problem, you cannot predict it and update the UI accordingly to prevent the user from doing something that won’t work. The technique described here can also be used at startup time to ask the user whether he’d like to install the missing package, you can then simply redirect him to the Android Market by using the appropriate URI.
Rescanning all your books ISBN are you? ;-)
Indeed, for the people that went to devoxx,
the ‘click’ is easily made.
Can’t wait for the final version ;-)
Anyway, great to see you pick up a bit of your older blogging style.
Handy suggestion. -1 for greying things out though (its often hard to tell just why something is disabled).
I debated against myself over this many times. My first solution was not greying out the menu but letting the user click it and then showing a message explaining what went wrong. I however find this even more frustrating: if I cannot do something, why does the UI let me do it in the first place? I could also remove the menu items, but it’s even worse, since the menu would change depending on unknown factors (kinda like greying out but at least you don’t change the layout.)
When the app is done, it will include a brief tutorial explaining the why and how of these items. Hopefully it will be clear enough.
Why not instead change the text of the menu item? Keep the menu item in the same position, but instead of using text like “Scan…”, use “Install scanner…” or “Learn about scanner…”.
welcome to maxemaos
This technique is used in Download Manager (one of the built in applications) to determine if it should bother downloading a requested link.
Hi Romain, although i think this check is better than nothing - i still have some concerns about using intents. As an example ( you might already read this on Dev forum ) - i want to share text via email and SMS. This should work with standard Messaging app as soon as cupcake is out
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, Body + emailTagLine);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, Title);
sendIntent.putExtra(”sms_body”, smsBody);
Well as you can see , i had to specify different extras for sms and email. Developer has a flexibility , even using “standard” action to put any extras. And not necessarily default one. And therefore to “choose and use” intents not only i need to know if intent is available but also set of extras to pass. And currently i don’t think i can get this info , unless source is open or developer shared api.
I follow your blog for quite a long time and should tell that your posts are always valuable to readers.