32

Is there a way to check and see if an Activity exists on your device? If I have a youtube video link I want to specify it open in the YouTube PlayerActivity. However, I don't want to crash if for some reason they don't have it.

Is there a way to check and see if the activity exists? I don't think I can catch the runtime exception since startActivity() doesn't throw it.

stormin986
  • 7,672
  • 15
  • 42
  • 54

5 Answers5

84

You could create an Intent object with necessary component info and then check if the intent is callable or not.I stumbled upon this snippet here on SO, don't have the link to the actual thread.

private boolean isCallable(Intent intent) {
        List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, 
            PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
}
Samuh
  • 36,316
  • 26
  • 109
  • 116
23

This is the simplest way to do this:

boolean activityExists = intent.resolveActivityInfo(getPackageManager(), 0) != null;

It is also the one recommended by Google:

To first verify that an app exists to receive the intent, call resolveActivity() on your Intent object. If the result is non-null, there is at least one app that can handle the intent and it's safe to call startActivity(). If the result is null, you should not use the intent and, if possible, you should disable the feature that invokes the intent.

Malcolm
  • 41,014
  • 11
  • 68
  • 91
  • 2
    Returns true for activity that doesn't exist. I'm afraid this is not the best solution. I've used the isCallable method posted in another answer and it works as expected. – tomrozb Jun 11 '14 at 06:07
  • @toro What did `intent.resolveActivity(getPackageManager())` return then? If this method really returns an non-existing activity, this definitely looks like a bug. Besides, according to Google documentation, `List list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);` does exactly the same, except it doesn't just return the activity with the top priority. – Malcolm Jun 11 '14 at 15:12
  • 1
    I have the intent created like this: `intent.setComponent(new ComponentName("package", "package.activity-name));` What does it return you ask - `ComponentInfo{"package"/"package.activity-name}`. But in fact the activity doesn't exist so calling this intent will end up in a runtime crash. – tomrozb Jun 12 '14 at 10:02
  • @toro Oh, I see what's happening. I've looked into the source code, this method doesn't check if the set component name is valid. I've updated the answer. Try this again, should work now. – Malcolm Jun 13 '14 at 16:55
8

I ended up doing:

        Intent intent = new Intent();
        intent.setClassName( "com.google.android.gsf", "com.google.android.gsf.login.AccountIntroActivity" );

        if(getContext().getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
            getContext().startActivity( intent );
        } else {
            getContext().startActivity(new Intent(Settings.ACTION_ADD_ACCOUNT));
        }

This ensures that the google-specific Add Account intent exists, and if not, falls back on the general more general ACTION_ADD_ACCOUNTS.

user979247
  • 371
  • 3
  • 3
4

I don't think I can catch the runtime exception

Actually, this works:

try {
    startActivity(new Intent(..));
} catch (ActivityNotFoundException e) {
    Toast.makeText(this, "Not installed.", LENGTH_SHORT).show();
}
yanchenko
  • 56,576
  • 33
  • 147
  • 165
  • 6
    This works of course, but it is generally considered a bad practice to do it like this. The answer by Samuh (http://stackoverflow.com/a/2879403/15695) is the correct way to do this. – BoD Sep 12 '12 at 15:51
  • Not only is it bad practice, but if you need to use startActivityForResult there are some serious efficiency/freezing problems on devices I tested on. – Mike Holler Oct 21 '15 at 21:31
0

Here's how I check if an Activity is available on the device:

        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tell//:" + phoneNumber));

        PackageManager manager = context.getPackageManager();
        List<ResolveInfo> activities = manager.queryIntentActivities(
                intent, 0);
        if (!manager.hasSystemFeature(
                PackageManager.FEATURE_TELEPHONY) || activities == null || activities
                .size() < 1) {
            Toast.makeText(
                    context,
                    "Sorry, there were no apps that worked with that request.",
                    Toast.LENGTH_SHORT).show();
        } else {
            context.startActivity(intent);
        }
Sakiboy
  • 7,252
  • 7
  • 52
  • 69