views:

68

answers:

1

I want to use addIntentOptions to drive my menus when ever possible. This seems the cleanest way to provide them. Rather than explicitly detailing activities, simply ask for a menu listing all the activities which are available for my data item.

So I'm trying to put together a context menu for a ListView. It works great. Only problem is that I have an activity that has two intents that consume my data type, and only the first shows up.

The activity in question in AndroidManifest.xml

<activity android:name=".ui.MyActivity" android:label="The title">
    <intent-filter android:label="First context label">
        <action android:name="com.sample.action.FIRST_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="myscheme" />
    </intent-filter>
    <intent-filter android:label="Second context label">
        <action android:name="com.sample.action.SECOND_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="myscheme" />
    </intent-filter>
</activity> 

The code to generate the context menu

@Override
public void onCreateContextMenu(ContextMenu menu, View view, 
        ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, view, menuInfo);

    Uri uri = Uri.fromParts("myscheme", getOpaqueUriOfSelectedItem(view), null)

    Intent intent = new Intent(null, uri);
    intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
            0, // Menu group to which new items will be added
            0, // Unique item ID (none)
            0, // Order for the items (none)
            this.getComponentName(), // The current Activity name
            null, // Specific items to place first (none)
            intent, // Intent created above that describes our requirements
            0, // Additional flags to control items (none)
            null); // Array of MenuItems that correlate to specific items
                    // (none)
}

As I say, the first intent of the activity shows up in the context menu and behaves like a dream. But I don't see the second intent, and I see no good reason it shouldn't show up. If Android only allows one intent with a particular category per activity, that's a pretty lame restriction.

I can see myself building a dummy activity that simply hands off to MyActivity. But that's clumsy and I'd like to avoid it if possible.

EDIT: Looking at the intent that is passed through to an activity from a context menu (or option menu, presumably), even if both intents showed up in the menu, the activity wouldn't have enough information to tell which intent was selected, as within the activity getIntent().getAction() is null.

This seems like an unfortunate oversight. Surely it isn't that unusual to have an activity that can consume a type of data in more than one way?

Unless one of you kind folk know something I've missed, it looks like I'm going to be creating my dummy activities.

EDIT: As CommonsWare suggested, I tried using queryIntentActivityOptions. I added in this code before menu.addIntentOptions in my code above.

PackageManager pm = getPackageManager();
final List<ResolveInfo> available = 
    pm.queryIntentActivityOptions(this.getComponentName(), null, intent, 0);

And in the debugger I found that available didn't include both of the available intents for MyActivity. So the issue isn't within addIntentOptions, it's deeper, within queryIntentActivityOptions somewhere.

A: 

My approach cannot work because queryIntentActivityOptions(), and the methods that call it, don't work in the way needed for my approach.

For my approach to work, you would need to get a result per intent-filter matched, which could result in multiple results per activity. Also, you would need to get information about which intent-filter matched in the result, specifically the action of the intent-filter.

However queryIntentActivityOptions() doesn't find intent-filters, it finds activities with at least one matching intent-filter. Meaning you only get one result per activity. The result also provides no information about the intent-filter that matched your intent.

This approach makes sense, but it's a shame that it doesn't allow for an activity to provide multiple ways to consume a particular intent.

So my workaround is to create fake activities for any activity with more than one action, that then hand off to the real activity.

So the sample manifest I included in the question would become

<activity android:name=".ui.MyActivity" android:label="The title" />
<activity android:name=".ui.MyActivityFirstAction" android:label="First action">
    <intent-filter android:label="First context label">
        <action android:name="com.sample.action.FIRST_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="myscheme" />
    </intent-filter>
</activity>
<activity android:name=".ui.MyActivitySecondAction" android:label="Second action">
    <intent-filter android:label="Second context label">
        <action android:name="com.sample.action.SECOND_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:scheme="myscheme" />
    </intent-filter>
</activity>

MyActivityFirstAction and MyActivitySecondAction would simply call MyActivity with the appropriate action and data.

I don't really like this scheme that much, but it still keeps all the actions that are in context menus defined in XML data rather than in code, and allows me to use addIntentOptions().

I still consider addIntentOptions() very tidy, and even if CommonTasks tells me that Google have been backpedaling from it, I will keep using it until I come across issues.

EDIT: As CommonsWare suggests, it would also be possible to create your own library for doing this in a non-hackish fashion. As I end up with more applications, I will probably move in this direction (unless I find an existing method I like better :-) ).

SamStephens