Advertisement
Android Phones

Android Intent Filter With Code: 2026 Guide (Examples, Demo App + Fixes)

Aditya SinghUpdated
Android intent filter code example in AndroidManifest.xml

An intent filter is a short block of XML in AndroidManifest.xml that tells Android: "send this kind of request to my app." It is how your app joins the share sheet, opens a link from Chrome, or appears on the home screen. Every filter needs an <action>. Almost every filter also needs android.intent.category.DEFAULT and an explicit android:exported value — miss either one and your app either fails to build or silently never appears.

Here is why this guide exists. We read the top-ranking intent filter tutorials before writing it. Most of them show manifest XML that will not build on Android 12 or newer, because they never mention android:exported. None of them explain the strict matching rule Android 13 added. None show you how to test a filter with adb. This guide gives you code that compiles against Android 16 (API 36), in both Kotlin and Java, plus the fix list for the question people actually ask second: "my filter is right there in the manifest, so why is nothing happening?"

In a hurry? Jump to: copy-paste examples · full demo app (5 steps) · attribute reference tables · rules that changed (2021–2026) · why your filter is not working · test it with adb

What Is an Intent Filter, in Plain English?

Think of your phone as a busy office. An intent is a note that says "someone please open this PDF." An intent filter is the sign on your office door that says "I open PDFs."

Android reads every door sign on the phone, finds the ones that match, and either opens your app straight away or shows a chooser so the user picks.

There are two kinds of intents:

  • Explicit intent — you name the exact class to open. Used inside your own app. No filter needed.
  • Implicit intent — you describe what you want done ("share this text") and let Android find an app. Intent filters exist only for this kind.

If you only move between your own screens, you do not need intent filters. You need them when you want to talk to other apps — or let other apps talk to you.

The 3 Tests Every Intent Must Pass

When an implicit intent goes out, Android checks it against your filter three times. Your app wins only if it passes all three.

TestWhat Android checksThe rule that trips people up
ActionDoes the intent's action match one <action> in your filter?A filter with zero actions matches nothing. Ever.
CategoryDoes your filter contain every category the intent carries?startActivity() silently adds CATEGORY_DEFAULT. If you did not declare it, you lose. Your filter may list extra categories; that is fine.
DataDoes the URI (scheme, host, port, path) and MIME type match?URI and MIME type are judged together. An intent with no data passes only if your filter declares no <data> at all.

The category test is where almost everyone loses an afternoon. Remember one line:

If you want other apps to reach you, declare android.intent.category.DEFAULT. The only exception is the launcher entry, which uses MAIN + LAUNCHER instead.

Copy-Paste Intent Filter Examples That Work in 2026

Every block below builds against API 36. Paste them straight into AndroidManifest.xml.

1. The launcher icon (every app has this one)

<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

This is the only activity that should be exported="true" by default. It is the front door.

2. Show up in the share sheet (receive text and images)

<activity
    android:name=".ShareActivity"
    android:label="Save to My App"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
        <data android:mimeType="image/*" />
    </intent-filter>

    <!-- Multiple items is a different action, so it gets its own filter. -->
    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>

The android:label is worth setting. The share sheet sorts entries by the filter's label, and falls back to your app name when there is none — so the label quietly controls where you appear in the list.

3. Read what you were handed — Kotlin and Java

Kotlin:

class ShareActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_share)

        // Never assume the action is what you expect.
        when (intent?.action) {
            Intent.ACTION_SEND -> {
                if (intent.type == "text/plain") {
                    val shared = intent.getStringExtra(Intent.EXTRA_TEXT)
                    // Treat this as untrusted input. Validate before you use it.
                    showText(shared.orEmpty())
                }
            }
            Intent.ACTION_VIEW -> showLink(intent.data)   // deep links land here
            else -> finish()
        }
    }
}

Java, for the same activity:

public class ShareActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_share);

        Intent intent = getIntent();
        String action = intent.getAction();          // may be null
        String type = intent.getType();

        if (Intent.ACTION_SEND.equals(action) && "text/plain".equals(type)) {
            String shared = intent.getStringExtra(Intent.EXTRA_TEXT);
            showText(shared == null ? "" : shared);
        } else if (Intent.ACTION_VIEW.equals(action)) {
            showLink(intent.getData());
        } else {
            finish();
        }
    }
}

Note the equals() order in Java. Write Intent.ACTION_SEND.equals(action), not action.equals(...) — the action can be null and will crash you.

4. Open your website links inside your app (App Links)

<activity
    android:name=".DeepLinkActivity"
    android:exported="true">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https"
              android:host="www.androidhire.com"
              android:pathPrefix="/deals" />
    </intent-filter>
</activity>

Two categories here, not one. BROWSABLE is what lets a browser hand the link over.

autoVerify="true" only works if you also host a Digital Asset Links file at https://yourdomain.com/.well-known/assetlinks.json, served as application/json over HTTPS, containing your app's signing certificate fingerprint. Play Console gives you the exact snippet. Miss that file and Android quietly demotes your link to "ask the user every time."

A detail almost no tutorial mentions: on Android 15 and higher the system re-verifies your domains in the background, and a fix to assetlinks.json can take up to seven days to reach every device. Do not panic-refactor your manifest on day two.

5. A custom scheme (myapp://)

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="myapp" android:host="profile" />
</intent-filter>

Custom schemes are easy but nobody owns them. Any other app can claim myapp:// too. Use HTTPS App Links for anything carrying a token, an account ID, or a payment.

6. Broadcast receivers and services

A manifest-declared receiver, listening for a system broadcast:

<receiver
    android:name=".BootReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Register one at runtime instead, and Android 14+ forces you to say who can reach it:

ContextCompat.registerReceiver(
    context,
    myReceiver,
    IntentFilter("com.example.ACTION_SYNC_DONE"),
    ContextCompat.RECEIVER_NOT_EXPORTED   // or RECEIVER_EXPORTED
)

For services, do not use implicit intents at all. Android has thrown on implicit service intents since 5.0. Always name the class, or name the package:

val intent = Intent("com.example.ACTION_UPLOAD").apply {
    setPackage("com.example.otherapp")   // makes it explicit enough
}
context.startService(intent)

7. A custom action for your own apps (protected)

<activity
    android:name=".TodoActivity"
    android:exported="true"
    android:permission="com.example.permission.EDIT_TODO">
    <intent-filter>
        <action android:name="com.example.action.EDIT_TODO" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Use your package name as the prefix for custom actions. And add android:permission so only apps you trust can fire it — an exported component without a permission is open to every app on the device.

Advertisement

8. Sending the intent (the part most tutorials skip)

val sendIntent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "Check this out")
}

// Do NOT use resolveActivity() as your guard on Android 11+.
// Package visibility can make it return null even when an app
// exists, so your share button just dies silently.
try {
    startActivity(Intent.createChooser(sendIntent, "Share via"))
} catch (e: ActivityNotFoundException) {
    Toast.makeText(this, "No app can handle this", Toast.LENGTH_SHORT).show()
}

Sharing a file or image instead of text? You must grant read access on the URI, or the receiving app gets a SecurityException:

val shareImage = Intent(Intent.ACTION_SEND).apply {
    type = "image/jpeg"
    putExtra(Intent.EXTRA_STREAM, contentUri)      // from a FileProvider
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(shareImage, "Share image"))

And if you really do need to check for a handling app first, declare what you are allowed to see (Android 11+):

<!-- top level of AndroidManifest.xml, next to <application> -->
<queries>
    <intent>
        <action android:name="android.intent.action.SEND" />
        <data android:mimeType="text/plain" />
    </intent>
</queries>

Without that <queries> block, resolveActivity() and queryIntentActivities() return nothing on Android 11 and up — and the feature looks broken for no visible reason. The system chooser is exempt, which is one more reason to prefer createChooser().

Build a Working Demo App in 5 Steps

This is a complete two-screen app. MainActivity shares text out. ReceiverActivity catches text shared from any app. Build it once and the whole idea clicks.

Step 1 — the layout (res/layout/activity_main.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="24dp">

    <EditText
        android:id="@+id/inputText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Type something to share" />

    <Button
        android:id="@+id/shareButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Share" />
</LinearLayout>

Step 2 — send the intent (MainActivity.kt):

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val input = findViewById<EditText>(R.id.inputText)
        findViewById<Button>(R.id.shareButton).setOnClickListener {
            val send = Intent(Intent.ACTION_SEND).apply {
                type = "text/plain"
                putExtra(Intent.EXTRA_TEXT, input.text.toString())
            }
            try {
                startActivity(Intent.createChooser(send, "Share with"))
            } catch (e: ActivityNotFoundException) {
                Toast.makeText(this, "Nothing can handle that", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

Step 3 — receive the intent (ReceiverActivity.kt):

class ReceiverActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_receiver)

        val label = findViewById<TextView>(R.id.resultText)
        if (intent?.action == Intent.ACTION_SEND && intent.type == "text/plain") {
            label.text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: "Empty"
        } else {
            label.text = "Opened directly, no shared text"
        }
    }
}

Step 4 — wire up the manifest. This is the step every broken tutorial gets wrong:

<application ... >

    <activity
        android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity
        android:name=".ReceiverActivity"
        android:label="Send to Demo App"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
    </activity>

</application>

Step 5 — run it. Install the app, then open Chrome or Google Keep, select some text, and tap Share. "Send to Demo App" now appears in the share sheet. Tap it and your text shows up on the second screen.

Want proof without leaving your desk? Fire the same intent from the terminal with the adb commands below.

Intent Filter Reference Tables

<intent-filter> attributes

AttributeWhat it does
android:labelName shown in the chooser or share sheet. Also affects share sheet ordering. Falls back to the app name.
android:iconIcon shown in the chooser. Falls back to the activity or app icon.
android:priorityRanks this filter against others. On Android 16, ordered-broadcast priority only applies within your own process.
android:orderOrders filters within the same app when several match. Does not affect other apps.
android:autoVerifyAsks Android to verify you own the domain via assetlinks.json. App Links only.

<data> attributes (URI matching)

AttributeMatchesExample
android:schemeThe part before ://. Required before host works.https, myapp, content
android:hostDomain. A leading * wildcard is allowed.www.androidhire.com, *.example.com
android:portPort number. Only used if scheme and host are set.8080
android:pathExact full path match./deals/today
android:pathPrefixPath starts with this./deals
android:pathPatternSimple glob. .* and * only./user/.*/profile
android:pathSuffixPath ends with this..pdf
android:mimeTypeContent type. Wildcards allowed.text/plain, image/*

The actions and categories you will actually use

ConstantManifest stringUsed for
ACTION_MAINandroid.intent.action.MAINApp entry point. Pair with LAUNCHER.
ACTION_VIEWandroid.intent.action.VIEWOpen a link, map, file, or contact. The most common action of all.
ACTION_SENDandroid.intent.action.SENDShare one item. Needs a mimeType.
ACTION_SEND_MULTIPLEandroid.intent.action.SEND_MULTIPLEShare several items at once.
ACTION_SENDTOandroid.intent.action.SENDTOEmail or SMS to a specific address, via a mailto: or smsto: scheme.
ACTION_DIALandroid.intent.action.DIALOpen the dialer with a number. Needs no permission.
ACTION_CALLandroid.intent.action.CALLPlace a call directly. Needs CALL_PHONE permission.
ACTION_GET_CONTENTandroid.intent.action.GET_CONTENTLet the user pick a file or image.
ACTION_EDIT / ACTION_PICK...action.EDIT / ...action.PICKEdit an item, or pick one from a list.
CATEGORY_DEFAULTandroid.intent.category.DEFAULTRequired to accept implicit intents.
CATEGORY_LAUNCHERandroid.intent.category.LAUNCHERPuts an icon on the home screen.
CATEGORY_BROWSABLEandroid.intent.category.BROWSABLERequired so a browser can hand a link to you.
Advertisement

The Rules That Changed: Android 11 Through 16

If you copied an intent filter from an older blog post, this table explains why it broke.

VersionWhat changedWhat it means for you
Android 11 (API 30)Package visibilityYou must declare <queries> to see other apps. resolveActivity() may return null.
Android 12 (API 31)android:exported is requiredAny component with an intent filter must set it explicitly, or the build fails and the app cannot install.
Android 12 (API 31)PendingIntent mutabilityYou must pass FLAG_IMMUTABLE or FLAG_MUTABLE.
Android 13 (API 33)Strict filter matchingIntents from other apps reach an exported component only if they truly match its filter. Applies no matter what the sending app targets.
Android 14 (API 34)Receivers and pending intents tightenedRuntime-registered receivers must pass RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED; implicit intents to non-exported components throw.
Android 15 (API 35)App Links re-verified in backgroundVerification changes can take up to 7 days to reach all devices.
Android 16 (API 36)Intent redirect defense + broadcast priorityStronger built-in protection against intent redirection attacks. android:priority for ordered broadcasts is honored only within the same process.

Why this matters right now: from August 31, 2026, new apps and updates on Google Play must target Android 16 (API 36), with extensions available until November 1, 2026. Every rule above is already live for anyone shipping this year.

Why Your Intent Filter Is Not Working (11 Real Causes)

This is the section people actually search for. Find your symptom, apply the fix.

SymptomMost likely causeFix
Build fails on installMissing android:exportedAdd android:exported="true" or "false" to every component that has a filter.
App never appears in the share sheetNo DEFAULT categoryAdd <category android:name="android.intent.category.DEFAULT" />.
Links open in Chrome, not your appNo BROWSABLE category, or failed verificationAdd BROWSABLE, then check assetlinks.json is live and correct.
App Link verification says "none"Wrong SHA-256 fingerprint, a redirect, or wrong content-typeUse the Play Console app-signing fingerprint. No redirects. Serve as application/json.
Works on your phone, not on users' phonesAndroid Studio installs auto-approve linksTest a Play or sideloaded build and re-check verification state.
Share button does nothingresolveActivity() returning null (Android 11+)Use try/catch around startActivity(), or declare <queries>.
ActivityNotFoundException after a target SDK bumpAndroid 13 strict matchingMake the intent's action and categories match the filter exactly.
SecurityException when sharing an image or fileNo URI permission grantedAdd FLAG_GRANT_READ_URI_PERMISSION and use a FileProvider URI.
Filter matched but the data is emptyReading the wrong extraACTION_SEND uses EXTRA_TEXT / EXTRA_STREAM; ACTION_VIEW uses intent.data.
Two filters merged and now match too muchElements stacked in one <intent-filter>Android combines everything inside one filter. Split unrelated pairings into separate filters.
Receiver never fires on Android 14+Missing export flag at registrationPass RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED to registerReceiver().

The "merged filter" one is subtle and worth repeating. A filter holding two actions and two MIME types does not mean two rules — it means every combination is accepted. Want specific pairings only? Write separate <intent-filter> blocks.

One more rule that saves you from bug reports: do not copy intent filters that are not yours. Pasting another app's manifest entries makes your app volunteer for intents it cannot handle, and users see your icon in places it does not belong.

Test Your Intent Filter in 60 Seconds With adb

Do not guess. Fire an intent at your own app from the terminal.

# 1. Send a plain-text share intent
adb shell am start -a android.intent.action.SEND \
  -t text/plain --es android.intent.extra.TEXT "hello"

# 2. Open a deep link
adb shell am start -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d "https://www.androidhire.com/deals"

# 3. Check App Link verification status (Android 12+)
adb shell pm get-app-links com.your.package

# 4. See every filter the system has registered for your app
adb shell dumpsys package com.your.package | grep -A 20 "Activity Resolver"

Command 3 is the one that saves hours. If it prints none or 1024 next to your domain, your assetlinks.json is the problem — not your manifest. Force a re-check with:

adb shell pm verify-app-links --re-verify com.your.package

Command 4 is the reality check: if your filter is not in that dump, Android never registered it, and no amount of code changes will help.

Treat Every Incoming Intent as Untrusted

An intent filter is a doorbell, not a lock. Anything can ring it, including a malicious app.

  • Set android:exported="false" on anything that does not genuinely need outside access.
  • Add android:permission to exported components meant only for your own apps.
  • Validate extras before use. Never feed a received URI straight into a WebView, a file read, or SQL.
  • Never forward an intent you received to startActivity() unchecked — that is intent redirection, the exact attack Android 16 hardened against.
  • Use explicit intents for services. Implicit service intents have been blocked since Android 5.0.
  • Use FLAG_IMMUTABLE on PendingIntents unless you have a specific reason not to.

Quick Cheat Sheet

  • Launcher icon? MAIN + LAUNCHER + exported="true"
  • Share sheet? SEND + DEFAULT + <data android:mimeType>
  • Web link? VIEW + DEFAULT + BROWSABLE + autoVerify="true" + assetlinks.json
  • Broadcast receiver at runtime? RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED
  • Service? Explicit intent, or setPackage(). Never implicit.
  • Internal only? exported="false" and an explicit intent
  • Broke after a target SDK bump? Re-read the version table

How We Verified This Guide

We rebuilt every snippet here against API 36 (Android 16) in Android Studio and confirmed each one compiles and installs. That matters because the most common complaint about older intent-filter tutorials is that their XML no longer builds under the Android 12 exported rule — and when we read the current top-ranking guides for this topic before writing, most of them still omit android:exported entirely, and none of them cover App Links verification, the Android 13 matching rule, or how to test a filter with adb.

Each behavior claim was cross-checked against Google's official Intents and intent filters documentation, the <intent-filter> manifest reference, the Android 16 behavior changes notes, the App Links verification guide, and Google Play's published target API level requirements. Where a rule depends on who sends the intent versus who receives it — a distinction most tutorials blur — we followed Google's wording exactly.

Bottom line: an intent filter is three small tests — action, category, data — wrapped in XML that Android has tightened six times since 2020. Declare your action, add DEFAULT (plus BROWSABLE for links), set android:exported on purpose, then prove it works with adb shell am start before you ship. Next step: build the 5-step demo app above, run the adb command, and watch your app appear in the share sheet. Then keep going with our guide to sending email in Android using an intent, or brush up on Android runtime permissions and the fragment lifecycle while you are in the manifest.

Frequently Asked Questions

What is an intent filter in Android?

An intent filter is an XML block in AndroidManifest.xml that tells Android which implicit intents your activity, service, or broadcast receiver can handle. It is how your app appears in the share sheet, opens links from a browser, or shows up on the home screen. Each filter can contain <action>, <category>, and <data> elements, and an incoming intent must pass all three tests to reach your component.

What is the difference between an intent and an intent filter?

An intent is the request ('open this link', 'share this text'). An intent filter is your app's advertisement saying it can handle that kind of request. Intents are created in Kotlin or Java at runtime; intent filters are declared in AndroidManifest.xml at build time. Explicit intents name a class directly and ignore filters completely; only implicit intents are matched against filters.

Why is my intent filter not working?

The most common causes are: a missing android.intent.category.DEFAULT category, a missing BROWSABLE category on deep links, no android:exported value (required since Android 12), a failed assetlinks.json verification for App Links, or an intent whose action and categories do not exactly match the filter. Android 13 and higher only deliver an external intent to an exported component when it genuinely matches the declared filter. Run 'adb shell pm get-app-links your.package' to check link verification, and 'adb shell dumpsys package your.package' to confirm the filter was registered at all.

Do I need android:exported on every intent filter?

Yes. Apps targeting Android 12 (API 31) or higher must set android:exported explicitly on every activity, service, and broadcast receiver that declares an intent filter. If you leave it out, the build fails and the app will not install. Use true only when other apps genuinely need access; use false for everything internal.

Can an intent filter have more than one action?

Yes, but be careful. Android treats everything inside a single <intent-filter> as combinable, so two actions and two MIME types means all four combinations are accepted. If you only want specific pairings, write separate <intent-filter> blocks instead of stacking elements into one.

What is autoVerify in an intent filter?

android:autoVerify="true" tells Android to check whether you really own the domain in your <data> element. Android fetches https://yourdomain.com/.well-known/assetlinks.json and compares your app's signing certificate fingerprint. If it matches, your links open directly in your app with no chooser dialog. On Android 15 and higher the system re-verifies periodically in the background, and changes can take up to seven days to reach all devices.

How do I test an intent filter without another app?

Use adb. Run 'adb shell am start -a android.intent.action.SEND -t text/plain --es android.intent.extra.TEXT "hello"' to fire a share intent, or 'adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://yourdomain.com/path"' for a deep link. Use 'adb shell pm get-app-links your.package' to see App Link verification status, and 'adb shell dumpsys package your.package' to confirm the system registered your filter.

Are intent filters a security feature?

No. An intent filter is a doorbell, not a lock. Any app can craft an intent that matches your filter, so always validate incoming data, set android:exported="false" on internal components, add android:permission to exported ones meant only for your own apps, never forward a received intent without checking it, and use FLAG_IMMUTABLE on PendingIntents. Android 16 adds default protection against intent redirection attacks, but your own validation is still required.

Can I use an intent filter for a service or broadcast receiver?

Broadcast receivers, yes. Services, effectively no. Implicit intents to services have been blocked since Android 5.0, so always name the class or call setPackage() on the intent. For manifest-declared receivers you still write a normal <intent-filter> with android:exported set. For receivers registered at runtime, Android 14 and higher requires you to pass RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED to registerReceiver().

What is the difference between a deep link, an App Link, and a custom scheme?

A custom scheme like myapp:// is the simplest, but nobody owns it, so any other app can claim the same scheme. A plain HTTP deep link works but usually shows a chooser dialog. An Android App Link is an HTTPS link with android:autoVerify="true" plus a matching assetlinks.json on your domain, so Android confirms you own the site and opens your app directly with no dialog. Use App Links for anything carrying a token, account ID, or payment.

Why does resolveActivity() return null on newer Android versions?

Because of package visibility, added in Android 11 (API 30). Your app can only see other packages you declare in a <queries> element in the manifest. Old sample code that guards a share button with 'if (intent.resolveActivity(packageManager) != null)' now fails silently on many devices. Either declare <queries>, or drop the check and wrap startActivity() in a try/catch for ActivityNotFoundException. The system chooser created by Intent.createChooser() is exempt from these restrictions.

Which Android version rules affect intent filters in 2026?

Android 11 added package visibility (resolveActivity can return null), Android 12 made android:exported mandatory, Android 13 added strict filter matching for external intents, Android 14 required RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED on runtime-registered receivers, Android 15 re-verifies App Links in the background, and Android 16 hardened intent redirection and limited android:priority ordering to a single process. New apps and updates on Google Play must target API 36 from August 31, 2026, with extensions available until November 1, 2026.

Android DevelopmentCodeAndroid

Related Articles

Advertisement