Zygote
Builder trackbeginner

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.

3 minAPI 21–37 · 17verified 2026-10-27

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#

  1. Create the project

    Create a new Compose project with an empty activity, package com.example.intentexplorer.

    You should seeA new Compose project that runs.
  2. Add the input state

    Hold the action and the URI in rememberSaveable strings, and render two OutlinedTextFields. Default them to android.intent.action.VIEW and https://example.com.

    You should seeTwo editable fields on screen.
  3. Build the intent

    Create an Intent(action) and call setData only when the URI is non-empty. A VIEW intent with no data is valid but resolves differently; the guard makes the behaviour explicit.

    You should seeNo crash when the URI is empty.
  4. 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}")
    }
  5. Query the package manager

    Use queryIntentActivities on 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.1

    You should seeA list of resolve infos, possibly empty.
  6. 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.
  7. 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.
  8. Launch the first result

    Build an explicit intent from the first result's component name and call startActivity. Catch the ActivityNotFoundException and show it: the gap between "resolved" and "launchable" is a real case.

    You should seeThe handler opens.
  9. 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.
Compare with the shell
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 -3
Example 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#

  1. Add a category field and see how adding CATEGORY_DEFAULT changes the result set.
  2. Add a MIME type field and test text/plain and image/*.
  3. Show the intent filters behind each result by reading them from the package manager's getIntentFilter data, 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.

  1. docsPackage visibility filtering on Androiddeveloper.android.comaccessed 2026-10-27↩
  2. docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩
  3. docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩
  4. docs活動簡介 | App architecturedeveloper.android.comaccessed 2026-10-27↩