B02 — Intent explorer
Build a small app that takes an action and a data URI, asks the package manager what would handle it, and shows the result. Intent resolution stops being abstract when your own name appears in the list.
An implicit intent is a request, not a call. The system resolves it against installed packages, and the result is a list you have never seen. This project shows you the list.
What you are building#
A single screen with two text fields — an action and a data URI — a "resolve" button, and a list of the components that would handle the intent. A second button actually launches the first result, so you can see resolution and execution side by side.
Steps#
- Create the project
Create a new Compose project with an empty activity, package
com.example.intentexplorer.You should seeA new Compose project that runs. - Add the input state
Hold the action and the URI in
rememberSaveablestrings, and render twoOutlinedTextFields. Default them toandroid.intent.action.VIEWandhttps://example.com.You should seeTwo editable fields on screen. - Build the intent
Create an
Intent(action)and callsetDataonly when the URI is non-empty. AVIEWintent with no data is valid but resolves differently; the guard makes the behaviour explicit.You should seeNo crash when the URI is empty. - Query the package manager
Use
queryIntentActivitieson the package manager and hold the result in state. On Android 11 and later, visibility rules apply: an app cannot see every package by default.1You should seeA list of resolve infos, possibly empty. - Declare visibility for the demo
Add a
<queries>element to the manifest for the actions you want to explore. This is the modern replacement for the old "see all packages" behaviour, and it is the most instructive part of the project: the manifest is part of the question.You should seeThe query returns more results than before. - Render the results
Show the package name and the class name for each result. If the list is empty, show an explicit "no handler" state rather than an empty screen.
You should seeOne row per component, with package and activity name. - Launch the first result
Build an explicit intent from the first result's component name and call
startActivity. Catch theActivityNotFoundExceptionand show it: the gap between "resolved" and "launchable" is a real case.You should seeThe handler opens. - Log the resolution
Log the result count and the first component, so the run is visible in logcat as well as on screen.
You should seeA logcat line with the count and the first component.
The resolution query and the result:
val intent = Intent(action).apply { data?.let { setData(Uri.parse(it)) } }
val results = packageManager.queryIntentActivities(intent, 0)
results.forEach {
Log.i("IntentExplorer", "${it.activityInfo.packageName}/${it.activityInfo.name}")
}adb shell cmd package resolve-activity --brief -a android.intent.action.VIEW -d https://example.com
adb logcat -s IntentExplorer
adb shell pm list packages -3Example output
Illustrative output — results depend on the device.Verification#
The app should reproduce the shell's answer. Run the same intent through am start and through your
explorer and compare: the component names should match when there is a single handler, and the
explorer should show multiple entries when the shell shows a chooser.
What you learned#
- An implicit intent is resolved, not routed. There is no registry of intents; the package manager searches the installed packages.
- Package visibility filters the answer. Since Android 11, the question "what can handle this?" depends on what the caller is allowed to see.
- Resolution and launch are separate steps. A component can resolve and still fail to start.
- The manifest declares intent. Adding
<queries>changes the result without changing the code.
Read Intents and filters for the matching rules, and Lab 8 — Intents for the shell equivalents.
Extend it#
- Add a category field and see how adding
CATEGORY_DEFAULTchanges the result set. - Add a MIME type field and test
text/plainandimage/*. - Show the intent filters behind each result by reading them from the package manager's
getIntentFilterdata, so the list explains itself.
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsPackage visibility filtering on Androiddeveloper.android.comaccessed 2026-10-27↩
- docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩
- docsApp Linksdeveloper.android.comaccessed 2026-10-27↩
- docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩
- docs活動簡介 | App architecturedeveloper.android.comaccessed 2026-10-27↩