Alright, I made it. I should warn that this is a lengthy email - I
might as well have written code instead of this, it would've taken
more or less the same amount of time ;) But I suppose open discussion
is important at this point.
First of all, the library. You mention an Application subclass - I
think that should definitely be there as a convenience thing, but
thinking about this again, I'd to split the functionality you
discussed as part of that class's onCreate() implementation into a
few public functions. Who knows, people may have good reasons for not
wanting to subclass from our Application class. But the principle
remains, that's just a detail.
I've looked into adding a uses-intent tag, and into how we could use
the existing meta-data tag to achieve our end, and ended up preferring
something very similar to what the SearchManager in Android 1.6+ does.
That is, within the <application> scope in an app's
AndroidManifest.xml, something like this should be included:
<meta-data android:name="openintents.intents.mandatory"
android:resource="@xml/foo" />
(I have no emotional attachment whatsoever to the name I picked above.
It just needs to be well-defined. Please make suggestions on this and
other names I'll use.)
The file res/xml/foo.xml then needs to contain something that defines
all the intents the package requires. Just listing actions won't cut
it, I'm afraid - we'll need to define the intents pretty much in the
same detail they're used in code, or else the dependency resolution
won't work well enough.
We've already got most of the format for defining intents the
<intent-filter> tags used for Activities, etc. The main differences
to <intent-filter> would be:
a) Intents can be explicit, i.e. you'd need an optional <component>
bit, and
b) you *may* want to specify whether you're targetting an Activity,
a Receiver, or some other type of component.
As a result, I think the xml file should look something along the
lines of this here:
<?xml version="1.0" encoding="utf-8"?>
<mandatory-intents>
<!-- Implicit Intent -->
<intent component-type="activity">
<action android:name="android.intent.action.BAZ"/>
<data android:scheme="quux" />
</intent>
<!-- Explicit Intent -->
<intent component-type="receiver">
<component android:name="foo.bar.BazReceiver" />
</intent>
</mandatory-intents>
The component-type attribute is something of a convenience feature.
It'll just give a hint as to whether PackageManager's
queryIntentActivities() or queryBroadcastReceivers(), etc. should be
used to try and resolve whether the Intent can be served.
As a first stab at defining how to specify mandatory Intents, that
should do the trick, I think.
Of course, if an Intent cannot be served, then we'd like to prompt
the user to install packages that would. For that, we won't be able
to avoid the IntentManager that's been discussed before.
To the library (and to the rest of the system), I think IntentManager
should act as a ContentProvider that primarily defines one content URI
that returns package information in return for an Intent specification.
The unpleasant part here is that in order to specify an Intent
completely, we'll need a fair amount of query parameters for that URI,
with most of them optional - after all, we'll need to be able to
specify all the elements that the <intent> tag discussed above can
contain.
For e.g. '<action android:name="android.intent.action.BAZ"/>' that's
not too bad, it translates nicely enough to e.g.
'?action=android.intent.action.BAZ'. All of <data>'s possible
attributes will need to be listed separately, as e.g. 'data-scheme',
etc. All workable, but it doesn't make for very pretty URIs. I'm not
too upset about that, given that this isn't something user-visible.
The Cursor returned for that URI should define a number of fields
that make it possible to display a Market-app-agnostic selecton
dialog, similar to what ResolverActivity shows. Looking towards
Android Market's lists of apps as inspiration, the required fields
would be:
* Package name (TEXT NOT NULL, e.g.
com.example.app)
* Package display name (TEXT NOT NULL, e.g. ExampleApp)
* Package vendor (TEXT NOT NULL, e.g. OpenIntents)
* Package price (INTEGER, can be NULL for free apps, in cents)
* Currency (TEXT, can be NULL if price is NULL)
* Icon URI (TEXT, can be a content: URI or an http: URI. Might allow
this to be NULL, I don't really mind).
* Market package name (TEXT NOT NULL, e.g.
com.mymarket.app - I'll
get back to that later)
* Market display name (TEXT NOT NULL, displayed as the "source" of
the package - if nothing else, this'll give minimal branding/
credits to the vendors providing Market-like apps)
With that information, it'd be possible to create a selection dialog
that shows pretty much the same information as any of the package
lists you'd currently find in Android Market. The one thing that's
missing would be a rating field - I'm not sure if that's maybe a bit
too specific to Android Market, but wouldn't mind including it.
Given the package name of the app offered for installation, and the
package name of the Market-like app that's offering, we'll have just
enough information to instruct the Market-like app to install a
package.
Well, but there's more required. IntentManager also needs to retrieve
all this information from somewhere. So here's how I think those
things should be handled, and again I'm looking towards SearchManager
for inspiration.
What SearchManager (indirectly) does is scan installed packages for
specific metadata. More precisely, it constructs an Intent (in this
case ACTION_SEARCH), resolves packages that respond to it, and then
scans those packages for meta-data named "android.app.searchable".
That meta-data again points to an xml file with some definition of
how the package is searchable.
Using the same basic mechanism, we can search for packages that
respond to ACTION_PACKAGE_INSTALL - or possibly an action we define
ourselves. (Note: ACTION_PACKAGE_INSTALL is documented as a
"protected" action, so probably won't be the best choice - it's
likely better that we define our own.)
We can find the Activity that responds to it, and launch it with the
package name of the package to be installed when an entry in the
selection list is clicked.
But we should also look for meta-data. At the very least, this should
contain the content URI of a ContentProvider in the same package,
that can be queried for package information, so e.g.
<?xml version="1.0" encoding="utf-8"?>
<intent-resolver
content-uri="content://foo/bar/baz"
/>
Yes, with only one piece of data, that meta-data could be defined
inline in AndroidManifest.xml. By keeping the meta-data in a resource
file, we'll be able to extend it more easily later on, though.
Now that ContentProvider should in the first instance respond to the
same URI path as IntentManager itself, returning much the same data.
The only thing we don't strictly speaking need is the package name of
the Market-style app, because we could deduce that - but it'll be
simpler to require it, and simple to provide it.
Armed with that information, IntentManager can provide as good a
choice of options for satisfying a dependency as I think we can get.
Now there are a few things in all this that are slightly less cleanly
solved as I think would be good:
1. Currently, Android Market does not provide a ContentProvider
that we can query for packages. It does not respond to whatever
ACTION_PACKAGE_INSTALL-like action we define.
My personal view on this is that what we're building here *should*
be a standard that Market-like apps implement, if at all possible.
So just catering to what Android Market already does seems wrong
to me. Still, it'd be good if we could use it somehow.
I think with a little tweak to the fields the ContentProviders are
supposed to return we can easily create and ship a "fallback"
provider that will, if Android Market is installed, always reply
that it knows a package. When selecting this field in the
selection dialog, an external search should be launched.
Obviously for such an entry, the package name, vendor, price and
currency can't be returned. We could easily e.g. define that a
negative price means the data set refers to an external search.
Or we add a new field for that.
The display name and icon are still useful; those two bits of
info won't refer to a specific package, but rather describe the
external search being launched. Add an optional field with a
search URI, and we can launch a search in Market.
That's where the suggestion of stating what Intents a package
responds to in the package description comes in - I don't think
that that's a good solution in the longer term, but for hooking
up an external search in Android Market, it might be a good
starting point.
2. I've conveniently ignored that it seems pretty hard to find the
IntentFilters for a given Activity name. Yes, PackageManager
defines a GET_INTENT_FILTERS flag, but I can't seem to find any
IntentFilter-related data member in ActivityInfo or one of it's
ancestors. Maybe I'm just blind.
Until/unless Android opens up that information, I think the best
a Market-like app can do is copy & extend the private
PackageParser from AOSP and parse AndroidManifest.xml from each
package in it's repository.
That's not hard - all the code for parsing IntentFilters is there.
They're just not added to the ActivityInfo.
3. I've somewhat skipped over whether the ResolverActivity-like
dialog should be part of the library, or part of IntentManager.
I'm thinking it should be the latter, but I'm not too concerned
about that detail.
4. I've skipped discussing what the library should do if
IntentManager is not installed. IMO there's only two things it
can do:
a) If Android Market is installed, prompt the user to download
IntentManager from there, with a link to the appropriate
search URL.
b) If Android Market is not installed, provide a link to a
website where IntentManager can be downloaded & installed
from.
Unfortunately, if IntentManager does not yet exist on the system,
we're in a classic bootstrapping situation where whatever options
we offer won't be as nice as the ones available after we're
already bootstrapped.
I think betting on Android Market, with a fallback to a website
is pretty much the best that can be done.
5. Aside from our ACTION_PACKAGE_INSTALL-clone, we might also
optionally support an ACTION_PACKAGE_VIEW action. I'm thinking
many users would like to see whatever details the Market-like
app can show on a package before installing it.
6. I think it'll be perfectly possible to add a query parameter to
the IntentManager's URL that specifies a package name. In that
way, we could support resolving dependencies on specific packages.
It'd also require some extension to the meta-data specifying
dependencies, but that wouldn't be too big a change.
7. Dependency resolution as I've outlined it is fairly primitive.
Let's pick an example to illustrate that:
* Package A requred Intent I1 and Intent I2
* Package B responds to Intent I1
* Package C responds to Intent I2
* Package D responds to Intents I1 and I2
Clearly, with all other things being equal, installing Package D
should be preferrable, but the current protocol does not permit
specifying more than one Intent via the IntentManager's provided
URI.
That's mostly a problem with specifying Intents via query
parameters. I think other schemes than the one proposed above
would allow for specifying more than one Intent, but things'll
get uglier quickly. One option would be to e.g. define each
Intent as a query string:
I1: action=foo.bar&data-scheme=http
I2: action=baz.quux&category=blargh
Then, urlencode those query strings and append them to the
IntentManager's URI (broken across lines):
content://intent-manager/resolve
?intent1=action%3Dfoo.bar%26data-scheme%3Dhttp
&intent2=action%3Dbaz.quux%26category%3Dblargh
In order to determine what to install, the Cursor returned by
IntentManager must at minimum also define which intents each
package will respond to, e.g. by returning them query string
encoded like above.
But what is the caller meant to do with that information? Should
we try and compute all possible combinations of installations
that would satisfy all requirements, and order them by the number
of packages installed? Is that worth the hassle?
8. The point of whether a user should be offered free and paid apps
as equal can easily be solved as a setting to IntentManager, or
as a toggle in the ResolverActivity-like dialog.
Similarly, it would probably be good to provide settings for
IntentManager that list all available sources for packages, and
let the user select which they want to use.
As you can see, with that much text, I might as well have started
implementing already - but like I said in the beginning, I think
discussing stuff is necessary if we want to come up with something
that more than one Market-like app ends up interacting with.
Speaking of implementing stuff, I think I'll just go ahead with that
in the next few days anyway - the main reason is that I'll be tied up
with other work from January on again, so if I don't get some stuff
done now, I won't for a while. I'll post details on the code
repository and stuff when I get to that point, this email has been
long enough already.
If you made it to this point, you're entitled to a cookie.
Jens