### Install to Maven Local Source: https://github.com/airbnb/deeplinkdispatch/blob/master/RELEASING.md Installs the project artifacts to your local Maven repository for testing. ```bash ./gradlew publishToMavenLocal ``` -------------------------------- ### Setup Kapt Plugin Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Add the kotlin-kapt plugin to your project for Kapt support. Ensure this is applied before other plugins. ```groovy plugins { id("kotlin-kapt") } ``` -------------------------------- ### Configure Path-Segment Placeholders Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Use placeholders like `` in URI templates to allow runtime replacement of path segments. The mapping value must start with `/` or be an empty string to remove the segment. ```java // Annotation (in a library module) @DeepLink("foo://cereal.com//nutritional_info") public static Intent intentForNutritionalInfo(Context context) { return new Intent(context, NutritionActivity.class); } // DeepLinkActivity — supply mappings at runtime Map configurablePlaceholders = new HashMap<>(); // With value: matches foo://cereal.com/obamaos/nutritional_info configurablePlaceholders.put("brand", "/obamaos"); // Empty value: matches foo://cereal.com/nutritional_info (segment removed) // configurablePlaceholders.put("brand", ""); DeepLinkDelegate delegate = new DeepLinkDelegate( new AppModuleRegistry(), configurablePlaceholders ); delegate.dispatchFrom(this); finish(); ``` -------------------------------- ### Define WebDeepLink with Country Prefixes Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Example of a WebDeepLink annotation that uses country prefixes in hostnames to match a wider range of URLs, including specific country domains and general airbnb.com. ```java // Match all deeplinks that have a scheme starting with http and also match any deeplink that // starts with .airbnb.com as well as ones that are only airbnb.com @DeepLinkSpec(prefix = { "http{url_scheme_suffix}://{country_prefix}.airbnb.com", "http{url_scheme_suffix}://airbnb.com") @Retention(RetentionPolicy.CLASS) public @interface WebDeepLink { String[] value(); } ``` -------------------------------- ### Create Intent with Extras for Deep Link Method Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Add a Bundle parameter to your static method to access Intent extras. This example demonstrates modifying the Intent data using query parameters from the extras. ```java @DeepLink("foo://example.com/methodDeepLink/{param1}") public static Intent intentForDeepLinkMethod(Context context, Bundle extras) { Uri.Builder uri = Uri.parse(extras.getString(DeepLink.URI)).buildUpon(); return new Intent(context, MainActivity.class) .setData(uri.appendQueryParameter("bar", "baz").build()) .setAction(ACTION_DEEP_LINK_METHOD); } ``` -------------------------------- ### Inspect DeepLinkResult (Success Case) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Use `createResult()` in unit tests to simulate and inspect the outcome of a deep link dispatch. This example shows how to check for success, retrieve the URI, parameters, and the matched deep link entry. ```kotlin // Use createResult() directly in unit tests (inject a mock DeepLinkMatchResult) val delegate = DeepLinkDelegate(listOf(SampleModuleRegistry())) val testIntent = Intent(Intent.ACTION_VIEW, Uri.parse("dld://example.com/deepLink?qp=hello")) val result: DeepLinkResult = delegate.dispatchFrom(activity, testIntent) println(result.isSuccessful) // true println(result.uriString) // dld://example.com/deepLink?qp=hello println(result.parameters) // {qp=hello} println(result.deepLinkMatchResult?.deeplinkEntry?.uriTemplate) // dld://example.com/deepLink println(result.error) // "" on success ``` -------------------------------- ### Retrieve Query Parameters from Deep Link Intent Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Query parameters in the deep link URI are automatically parsed and available in the Intent extras. This example shows how to retrieve a query parameter named 'qp'. ```java @DeepLink("foo://example.com/deepLink") public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) { Bundle parameters = intent.getExtras(); if (parameters != null && parameters.getString("qp") != null) { String queryParameter = parameters.getString("qp"); // Do something with the query parameter... } } } } ``` -------------------------------- ### Programmatic URI Lookup with `findEntry()` Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Use `findEntry()` to get a `DeepLinkMatchResult` for a URI without starting an Activity. This is useful for testing or pre-flight validation. The `supportsUri()` method provides a quick boolean check. ```kotlin val deepLinkDelegate = DeepLinkDelegate(SampleModuleRegistry()) // Check if a URI is handled val match: DeepLinkMatchResult? = deepLinkDelegate.findEntry("dld://example.com/deepLink") if (match != null) { println("Matched: ${match.deeplinkEntry.uriTemplate}") println("Class: ${match.deeplinkEntry.className}") val params: Map = match.getParameters( DeepLinkUri.parse("dld://example.com/deepLink?qp=42") ) println("Params: $params") // {qp=42} } else { println("No match found") } // Quick boolean check val supported: Boolean = deepLinkDelegate.supportsUri("dld://example.com/deepLink") // Inspect all registered deep links deepLinkDelegate.allDeepLinkEntries.forEach { entry -> println("${entry.uriTemplate} → ${entry.className}") } // Find potential duplicates across all registries (useful after binary is built) val duplicates: Map> = deepLinkDelegate.duplicatedDeepLinkEntries duplicates.forEach { (entry, dupes) -> println("${entry.uriTemplate} is duplicated by: ${dupes.map { it.uriTemplate }}") } ``` -------------------------------- ### Dispatch Incoming Deep Link with BaseDeepLinkDelegate.dispatchFrom() (Kotlin) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt The central runtime method to extract a URI from an Activity's Intent, look it up in registries, start the matched Activity/TaskStackBuilder, and broadcast the result. Returns a DeepLinkResult for programmatic inspection. This example shows custom type converters and an error handler. ```kotlin // Called inside the transparent DeepLinkActivity (Kotlin example) class DeepLinkActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val typeConverters = TypeConverters().apply { put(ColorDrawable::class.java, TypeConverter { value -> when (value.lowercase()) { "red" -> ColorDrawable(Color.RED) "blue" -> ColorDrawable(Color.BLUE) else -> ColorDrawable(Color.TRANSPARENT) } }) } val deepLinkDelegate = DeepLinkDelegate( registries = listOf(SampleModuleRegistry()), typeConverters = { typeConverters }, errorHandler = object : ErrorHandler() { override fun duplicateMatch( uriString: String, duplicatedMatches: List ) { Log.w("DLD", "Duplicate match for $uriString: $duplicatedMatches") } }, typeConversionErrorNullable = { uri, type, value -> null }, typeConversionErrorNonNullable = { uri, type, value -> 0 } ) val result: DeepLinkResult = deepLinkDelegate.dispatchFrom(this) if (!result.isSuccessful) { Log.e("DLD", "Deep link failed: ${result.error}") } finish() } } ``` -------------------------------- ### Inspect DeepLinkResult (Failure Case) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Demonstrates how to inspect a failed deep link dispatch using `DeepLinkResult`. This example shows checking the `isSuccessful` flag and retrieving the error message when no URI is found in the intent. ```kotlin // Failure example val badResult = delegate.dispatchFrom(activity, Intent(Intent.ACTION_VIEW)) println(badResult.isSuccessful) // false println(badResult.error) // "No Uri in given activity's intent." ``` -------------------------------- ### Annotate Activity with Custom Deep Links Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Annotate an Activity with custom deep link annotations to specify which URIs it should handle. This example shows handling both app and web deep links. ```java // This activity is gonna handle the following deep links: // "app://airbnb/view_users" // "http://airbnb.com/users" // "http://airbnb.com/user/{id}" // "https://airbnb.com/users" // "https://airbnb.com/user/{id}" @AppDeepLink({ "/view_users" }) @WebDeepLink({ "/users", "/user/{id}" }) public class CustomPrefixesActivity extends AppCompatActivity { //... } ``` -------------------------------- ### Kotlin Static Method for Deep Link Intent Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md For Kotlin, annotate your static method with @JvmStatic. Use object declarations instead of companion objects. This example shows creating a default Intent. ```kotlin object DeeplinkIntents { @JvmStatic @DeepLink("https://example.com") fun defaultIntent(context: Context, extras: Bundle): Intent { return Intent(context, MyActivity::class.java) } } ``` -------------------------------- ### Fire Standard Deep Link Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Use this command to fire a standard deep link. Ensure the sample app's package name is included. ```bash am start -W -a android.intent.action.VIEW -d "dld://example.com/deepLink" com.airbnb.deeplinkdispatch.sample ``` -------------------------------- ### Generate Deep Link Documentation (KSP) Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Configure KSP arguments in build.gradle to generate a text file documenting all deep link annotations for Kotlin projects using KSP. ```groovy ksp { arg("deepLinkDoc.output", "${buildDir}/doc/deeplinks.txt") } ``` -------------------------------- ### Publish to Maven Central Source: https://github.com/airbnb/deeplinkdispatch/blob/master/RELEASING.md Run this command to publish artifacts to Maven Central. Ensure your Sonatype login and GPG signing key are configured. ```bash ./gradlew publishAllPublicationsToMavenCentral ``` -------------------------------- ### Generate Deep Link Documentation (Kotlin Kapt) Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Configure Kapt arguments in build.gradle to generate a text file documenting all deep link annotations for Kotlin projects. ```groovy kapt { arguments { arg("deepLinkDoc.output", "${buildDir}/doc/deeplinks.txt") } } ``` -------------------------------- ### Configure Mappings for Configurable Placeholders Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md When using configurable path segment placeholders, provide a runtime mapping of placeholder IDs to their allowed values when initializing `DeepLinkDelegate`. Failure to provide a mapping for a used placeholder will cause a runtime crash. ```java Map configurablePlaceholdersMap = new HashMap(); configurablePlaceholdersMap.put("type_of_cereal", "/obamaos"); DeepLinkDelegate deepLinkDelegate = new DeepLinkDelegate(new AppDeepLinkModuleRegistry(), new LibraryDeepLinkModuleRegistry(), configurablePlaceholdersMap); deepLinkDelegate.dispatchFrom(this); ``` -------------------------------- ### Configure KSP Plugin for DeepLinkDispatch Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Set up the KSP plugin in your project's build.gradle file by adding the classpath dependency. This is recommended for Kotlin projects for performance improvements. ```groovy buildscript { apply from: rootProject.file("dependencies.gradle") repositories { google() gradlePluginPortal() } dependencies { classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:" } } ``` -------------------------------- ### Apply KSP Plugin and Add Dependencies Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Apply the KSP plugin in the module's build.gradle file and add the DeepLinkDispatch and its processor dependency using the ksp configuration. Ensure at least one Kotlin source file is present for output generation. ```groovy plugins { id("com.google.devtools.ksp") } dependencies { implementation 'com.airbnb:deeplinkdispatch:x.x.x' ksp 'com.airbnb:deeplinkdispatch-processor:x.x.x' } ``` -------------------------------- ### Configure Incremental Annotation Processing and Doc Generation (KAPT) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Enable incremental processing and specify custom annotation classes for the KAPT processor. Optionally, configure an output file for deep link documentation in a text file. ```groovy // build.gradle — KAPT kapt { arguments { arg("deepLink.incremental", "true") arg("deepLink.customAnnotations", "com.example.AppDeepLink|com.example.WebDeepLink") arg("deepLinkDoc.output", "${buildDir}/doc/deeplinks.txt") } } ``` -------------------------------- ### Java Implementation of `DeepLinkHandler` Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Demonstrates implementing the `DeepLinkHandler` interface in Java. Path and query parameters are accessed via getter methods on the args object. ```java // Java implementation @WebDeepLink({"/java/{seg1}/{seg2}?show_taxes={show_taxes}"}) public class JavaProductHandler implements DeepLinkHandler { @Override public void handleDeepLink(@NonNull Context context, JavaProductHandlerArgs args) { Intent intent = new Intent(context, ProductActivity.class); intent.putExtra("seg1", args.getSeg1()); context.startActivity(intent); } } ``` -------------------------------- ### Generate Deep Link Documentation (Java Compile) Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Configure Java compilation tasks in build.gradle to generate a text file documenting all deep link annotations. ```groovy tasks.withType(JavaCompile) { options.compilerArgs << "-AdeepLinkDoc.output=${buildDir}/doc/deeplinks.txt" } ``` -------------------------------- ### Handle Deep Links with Configurable Placeholders Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md When using configurable path segments, provide a map of placeholder values to the DeepLinkDelegate constructor. The map keys should match the placeholder names in your deep link annotations. ```java @DeepLinkHandler({ AppDeepLinkModule.class, LibraryDeepLinkModule.class }) public class DeepLinkActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Configure a map for configurable placeholders if you are using any. If you do a mapping // has to be provided for that are used Map configurablePlaceholdersMap = new HashMap(); configurablePlaceholdersMap.put("your_values", "what should match"); // DeepLinkDelegate, LibraryDeepLinkModuleRegistry and AppDeepLinkModuleRegistry // are generated at compile-time. DeepLinkDelegate deepLinkDelegate = new DeepLinkDelegate(new AppDeepLinkModuleRegistry(), new LibraryDeepLinkModuleRegistry(), configurablePlaceholdersMap); // Delegate the deep link handling to DeepLinkDispatch. // It will start the correct Activity based on the incoming Intent URI deepLinkDelegate.dispatchFrom(this); // Finish this Activity since the correct one has been just started finish(); } } ``` -------------------------------- ### Configure Incremental Annotation Processing and Doc Generation (KSP) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Enable incremental processing and specify custom annotation classes for the KSP processor. Optionally, configure an output file for deep link documentation in Markdown table format. ```groovy // build.gradle — KSP (always incremental) ksp { arg("deepLink.incremental", "true") arg("deepLink.customAnnotations", "com.example.AppDeepLink|com.example.WebDeepLink") arg("deepLinkDoc.output", "${buildDir}/doc/deeplinks.md") // Markdown table } ``` -------------------------------- ### Fire Deep Link with Multiple Path Parameters Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md This command fires a deep link with multiple path parameters. The sample app's package name is not required. ```bash am start -W -a android.intent.action.VIEW -d "http://example.com/deepLink/123abc/myname" ``` -------------------------------- ### Add Kapt Dependencies Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Include the DeepLinkDispatch library and its processor dependency for Kapt projects. Replace 'x.x.x' with the actual version. ```groovy dependencies { implementation 'com.airbnb:deeplinkdispatch:x.x.x' kapt 'com.airbnb:deeplinkdispatch-processor:x.x.x' } ``` -------------------------------- ### Handle Deep Links with DeepLinkDelegate Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Annotate your Activity with @DeepLinkHandler and instantiate DeepLinkDelegate with your module registries to handle incoming deep links. ```java @DeepLinkHandler({ AppDeepLinkModule.class, LibraryDeepLinkModule.class }) public class DeepLinkActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // DeepLinkDelegate, LibraryDeepLinkModuleRegistry and AppDeepLinkModuleRegistry // are generated at compile-time. DeepLinkDelegate deepLinkDelegate = new DeepLinkDelegate(new AppDeepLinkModuleRegistry(), new LibraryDeepLinkModuleRegistry()); // Delegate the deep link handling to DeepLinkDispatch. // It will start the correct Activity based on the incoming Intent URI deepLinkDelegate.dispatchFrom(this); // Finish this Activity since the correct one has been just started finish(); } } ``` -------------------------------- ### Configure Incremental Annotation Processing (Kotlin Kapt) Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Configure incremental annotation processing and custom annotations for Kotlin projects using Kapt in build.gradle. ```groovy kapt { arguments { arg("deepLink.incremental", "true") arg("deepLink.customAnnotations", "com.airbnb.AppDeepLink|com.airbnb.WebDeepLink") } } ``` -------------------------------- ### Configure Incremental Annotation Processing and Doc Generation (Java) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Enable incremental processing and specify custom annotation classes for the Java annotation processor. Optionally, configure an output file for deep link documentation in a text file. ```groovy // build.gradle — Java annotation processor tasks.withType(JavaCompile) { options.compilerArgs += [ "-AdeepLink.incremental=true", "-AdeepLink.customAnnotations=com.example.AppDeepLink|com.example.WebDeepLink", "-AdeepLinkDoc.output=${buildDir}/doc/deeplinks.txt" ] } ``` -------------------------------- ### Fire Deep Link with Path Parameter Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md This command fires a deep link associated with a method and passes a path parameter. The sample app's package name can be omitted. ```bash am start -W -a android.intent.action.VIEW -d "dld://methodDeepLink/abc123" com.airbnb.deeplinkdispatch.sample ``` -------------------------------- ### Add Sonatype Snapshots Repository Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Configure your build.gradle file to include Sonatype's snapshots repository for accessing development versions of DeeplinkDispatch. ```groovy repositories { maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } } ``` -------------------------------- ### Handle Empty Configurable Path Segment Mapping Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md A mapping can be set to an empty string to effectively ignore that path segment. Ensure that empty configurable path segment placeholders are not the last element in a URL. ```java configurablePlaceholdersMap.put("type_of_cereal", ""); ``` -------------------------------- ### Deep Link Documentation Format Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md The expected format for the generated deep link documentation text file. ```text * {DeepLink1}|#|[Description part of javadoc]|#|{ClassName}#[MethodName]|##| * {DeepLink2}|#|[Description part of javadoc]|#|{ClassName}#[MethodName]|##| ``` -------------------------------- ### Configure Incremental Annotation Processing (KSP) Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Configure incremental annotation processing and custom annotations for Kotlin projects using KSP in build.gradle. KSP is always incremental. ```groovy ksp { arg("deepLink.incremental", "true") arg("deepLink.customAnnotations", "com.airbnb.AppDeepLink|com.airbnb.WebDeepLink") } ``` -------------------------------- ### Programmatic URI Lookup with BaseDeepLinkDelegate.findEntry() Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt This method allows you to find a deep link match for a given URI without initiating any Activity. It's useful for testing or validating URIs before they are handled. ```APIDOC ## `BaseDeepLinkDelegate.findEntry()` — Programmatic URI lookup Returns a raw `DeepLinkMatchResult?` for a URI string without starting any Activity. Useful in tests or for pre-flight URI validation. ```kotlin val deepLinkDelegate = DeepLinkDelegate(SampleModuleRegistry()) // Check if a URI is handled val match: DeepLinkMatchResult? = deepLinkDelegate.findEntry("dld://example.com/deepLink") if (match != null) { println("Matched: ${match.deeplinkEntry.uriTemplate}") println("Class: ${match.deeplinkEntry.className}") val params: Map = match.getParameters( DeepLinkUri.parse("dld://example.com/deepLink?qp=42") ) println("Params: $params") // {qp=42} } else { println("No match found") } // Quick boolean check val supported: Boolean = deepLinkDelegate.supportsUri("dld://example.com/deepLink") // Inspect all registered deep links deepLinkDelegate.allDeepLinkEntries.forEach { entry -> println("${entry.uriTemplate} → ${entry.className}") } // Find potential duplicates across all registries (useful after binary is built) val duplicates: Map> = deepLinkDelegate.duplicatedDeepLinkEntries duplicates.forEach { (entry, dupes) -> println("${entry.uriTemplate} is duplicated by: ${dupes.map { it.uriTemplate }}") } ``` ``` -------------------------------- ### Performance Benchmark Results Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Benchmark results for DeeplinkDispatch on a Pixel 2 running Android 11, testing deep link resolution performance with a large number of deep links. ```text Started running tests benchmark: 11,716 ns DeeplinkBenchmarks.match1 benchmark: 139,375 ns DeeplinkBenchmarks.match500 benchmark: 2,163,907 ns DeeplinkBenchmarks.newRegistry benchmark: 23,035 ns DeeplinkBenchmarks.match1000 benchmark: 152,969 ns DeeplinkBenchmarks.match1500 benchmark: 278,906 ns DeeplinkBenchmarks.match2000 benchmark: 162,604 ns DeeplinkBenchmarks.createResultDeeplink1 benchmark: 11,774 ns DeeplinkBenchmarks.parseDeeplinkUrl Tests ran to completion. ``` -------------------------------- ### Create Intent for Deep Link Method Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Annotate a public static method with @DeepLink to have DeepLinkDispatch create the Intent. This method returns a basic Intent for the deep link. ```java @DeepLink("foo://example.com/methodDeepLink/{param1}") public static Intent intentForDeepLinkMethod(Context context) { return new Intent(context, MainActivity.class) .setAction(ACTION_DEEP_LINK_METHOD); } ``` -------------------------------- ### Manifest Generation with KSP Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Use @DeepLink with KSP to automatically generate AndroidManifest.xml intent-filter entries. Requires KSP and specifying activityClassFqn. Custom actions and categories can be provided. ```kotlin import android.app.Activity import android.os.Bundle import android.content.Intent import com.airbnb.deeplinkdispatch.DeepLink @DeepLink("http{scheme(|s)}://example.{domain(com|de|ro)}/deepLink/{id}", "{scheme(foo|bar)}://{host(example|another-example)}.{domain(com|de|ro)}/anotherDeepLink", activityClassFqn = "com.example.MainActivity") class MainActivity : Activity { @Override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val intent : Intent = getIntent() if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) { val parameters : Bundle = intent.getExtras() val idString : String = parameters.getString("id") // Do something with idString } } } ``` -------------------------------- ### Configure Incremental Annotation Processing (Standard) Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Enable incremental annotation processing and register custom deep link annotations in your build.gradle file for standard Java compilation. ```groovy javaCompileOptions { annotationProcessorOptions { arguments = [ 'deepLink.incremental': 'true', 'deepLink.customAnnotations': 'com.airbnb.AppDeepLink|com.airbnb.WebDeepLink' ] } } ``` -------------------------------- ### Define Deeplink with Configurable Path Segment Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Use `` syntax within the deeplink URI to define a placeholder for a path segment. This allows the same deeplink definition to match different runtime values. ```java @DeepLink("foo://cereal.com//nutritional_info") public static Intent intentForNutritionalDeepLinkMethod(Context context) { return new Intent(context, MainActivity.class) .setAction(ACTION_DEEP_LINK_METHOD); } ``` -------------------------------- ### Custom DeepLinkSpec Annotations (Java) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Defines reusable, prefix-based custom annotations for deep links. Prefixes can include placeholder syntax for matching multiple variants. Usage involves annotating an Activity with these custom annotations. ```java import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.TaskStackBuilder; import com.airbnb.deeplinkdispatch.DeepLink; import com.airbnb.deeplinkdispatch.DeepLinkMethodResult; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; // Define a custom annotation for all "app://airbnb" deep links @DeepLinkSpec(prefix = { "app://airbnb" }) @Retention(RetentionPolicy.RUNTIME) public @interface AppDeepLink { String[] value(); } // Matches http:// and https:// for airbnb.com @DeepLinkSpec(prefix = { "http{scheme_suffix(|s)}://airbnb.com" }) @Retention(RetentionPolicy.RUNTIME) public @interface WebDeepLink { String[] value(); } // Multi-country, multi-subdomain prefix @DeepLinkSpec(prefix = { "http{url_scheme_suffix(|s)}://{prefix(|www.)}airbnb.{domain(com|de)}" }) @Retention(RetentionPolicy.CLASS) public @interface GlobalWebDeepLink { String[] value(); } // Usage — this activity handles: // app://airbnb/view_users // http://airbnb.com/users https://airbnb.com/users // http://airbnb.com/user/{id} https://airbnb.com/user/{id} @AppDeepLink({ "/view_users" }) @WebDeepLink({ "/users", "/user/{id}" }) public class CustomPrefixesActivity extends AppCompatActivity { /* ... */ } ``` -------------------------------- ### Type-Safe Handler Implementation (`DeepLinkHandler`) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Implement `DeepLinkHandler` for type-safe deep link handling. Define an args data class with `@DeeplinkParam` annotations for path and query parameters. Path parameters are non-nullable, while query parameters are nullable. ```kotlin // 1. Define the args data class — every constructor parameter must be annotated data class ProductHandlerArgs( @DeeplinkParam("id", DeepLinkParamType.Path) val id: Long, @DeeplinkParam("slug", DeepLinkParamType.Path) val slug: String, @DeeplinkParam("ref", DeepLinkParamType.Query) val referrer: String?, @DeeplinkParam("preview", DeepLinkParamType.Query) val preview: Boolean?, ) // 2. Implement the handler @DeepLink( "https://example.com/products/{id}/{slug}", activityClassFqn = "com.example.DeepLinkActivity" ) object ProductDeepLinkHandler : DeepLinkHandler { override fun handleDeepLink(context: Context, deepLinkArgs: ProductHandlerArgs) { // Fully typed — no casting, no bundle key strings val intent = Intent(context, ProductActivity::class.java).apply { putExtra("product_id", deepLinkArgs.id) putExtra("product_slug", deepLinkArgs.slug) deepLinkArgs.referrer?.let { putExtra("referrer", it) } } context.startActivity(intent) } } ``` ```kotlin // Inheritance chains are supported: abstract class BaseHandler : DeepLinkHandler { override fun handleDeepLink(context: Context, deepLinkArgs: T) { /* shared logic */ } } @DeepLink("dld://host/path") object ConcreteHandler : BaseHandler() ``` -------------------------------- ### Apply DeepLinkDispatch Gradle Plugin Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Apply the DeepLinkDispatch gradle plugin in your root build.gradle file to enable manifest generation. Ensure it's applied after Kotlin and Android plugins. ```groovy buildscript { ... dependencies { ... classpath "com.airbnb:deeplinkdispatch-gradle-plugin:$VERSION" } } ``` ```groovy apply plugin: 'com.airbnb.deeplinkdispatch.manifest-generation' ``` -------------------------------- ### Configure Activity with Manifest Generation Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Define your deep link Activity in the AndroidManifest.xml. When using KSP and manifest generation, intent filters are added automatically. ```xml ``` -------------------------------- ### Publish to Artifactory Repository Source: https://github.com/airbnb/deeplinkdispatch/blob/master/RELEASING.md Use this command to publish an internal release to an Artifactory repository. The '-PdoNotSignRelease=true' flag is optional and skips GPG signing for Artifactory releases. ```bash ./gradlew publishAllPublicationsToAirbnbArtifactoryRepository -PdoNotSignRelease=true ``` -------------------------------- ### Define Custom WebDeepLink Annotation with Placeholders Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Define custom annotations for web deep links that support placeholders in the scheme and host for flexible matching. Placeholders can include allowed values. ```java // Match all deeplinks which a scheme staring with "http". @DeepLinkSpec(prefix = { "http{url_scheme_suffix}://airbnb.com") @Retention(RetentionPolicy.CLASS) public @interface WebDeepLink { String[] value(); } ``` ```java // Match all deeplinks which a scheme staring with "http". @DeepLinkSpec(prefix = { "http{url_scheme_suffix(|s)}://{prefix(|www.)}airbnb.{domain(com|de)}") @Retention(RetentionPolicy.CLASS) public @interface WebDeepLink { String[] value(); } ``` -------------------------------- ### Wire the Dispatch Activity with @DeepLinkHandler Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Annotate a transparent Activity with @DeepLinkHandler to receive all incoming deep link Intents. This activity lists all @DeepLinkModule-annotated classes for aggregation by DeepLinkDelegate. It calls deepLinkDelegate.dispatchFrom(this) and finishes immediately. ```xml // AndroidManifest.xml (without manifest generation) // // // // // // // // ``` ```java @DeepLinkHandler({ SampleModule.class, LibraryDeepLinkModule.class, BenchmarkDeepLinkModule.class }) public class DeepLinkActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Optional: configurable path-segment replacements Map configurablePlaceholders = new HashMap<>(); configurablePlaceholders.put("configurable-path-segment", "/obamaos"); DeepLinkDelegate deepLinkDelegate = new DeepLinkDelegate( new SampleModuleRegistry(), new LibraryDeepLinkModuleRegistry(), new BenchmarkDeepLinkModuleRegistry(), configurablePlaceholders ); deepLinkDelegate.dispatchFrom(this); finish(); } } ``` -------------------------------- ### Register Activity for Single Deep Link Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Annotate an Activity with @DeepLink and a URI pattern to handle specific deep links. The library parses the URI and extracts parameters into a Bundle. ```java import android.app.Activity; import android.os.Bundle; import android.content.Intent; import com.airbnb.deeplinkdispatch.DeepLink; @DeepLink("example://example.com/deepLink/{id}") public class SampleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) { Bundle parameters = intent.getExtras(); String idString = parameters.getString("id"); // Do something with idString } } } ``` -------------------------------- ### Create Multiple DeepLinkModule Classes Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md If your application has multiple modules, create a @DeepLinkModule class in each to collect annotations into separate registry classes per module. ```java /** This will generate a LibraryDeepLinkModuleRegistry class */ @DeepLinkModule public class LibraryDeepLinkModule { } ``` -------------------------------- ### Create DeepLinkModule Class Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Annotate a class with @DeepLinkModule to generate a registry for your @DeepLink annotations. This is a new feature in DeepLinkDispatch v3. ```java /** This will generate a AppDeepLinkModuleRegistry class */ @DeepLinkModule public class AppDeepLinkModule { } ``` -------------------------------- ### Add DeepLinkDispatch Dependency Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Add the DeepLinkDispatch library to your project's build.gradle file using the implementation configuration. ```groovy dependencies { implementation 'com.airbnb:deeplinkdispatch:x.x.x' } ``` -------------------------------- ### Add Java Annotation Processor Dependency Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Include the DeepLinkDispatch library and its annotation processor dependency for Java projects. Replace 'x.x.x' with the actual version. ```groovy dependencies { implementation 'com.airbnb:deeplinkdispatch:x.x.x' annotationProcessor 'com.airbnb:deeplinkdispatch-processor:x.x.x' } ``` -------------------------------- ### Register Activity for Multiple Deep Links Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md An Activity can handle multiple deep link patterns by providing an array of URIs to the @DeepLink annotation. Parameters are extracted similarly to single deep links. ```java import android.app.Activity; import android.os.Bundle; import android.content.Intent; import com.airbnb.deeplinkdispatch.DeepLink; @DeepLink({"foo://example.com/deepLink/{id}", "foo://example.com/anotherDeepLink"}) public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) { Bundle parameters = intent.getExtras(); String idString = parameters.getString("id"); // Do something with idString } } } ``` -------------------------------- ### Method-level DeepLink Annotation (Kotlin) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Annotates a static method to handle deep links and return an Intent or TaskStackBuilder. Access query parameters via the Bundle. The DeepLinkMethodResult allows choosing between Intent and TaskStackBuilder at runtime. ```kotlin import android.content.Context import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.app.TaskStackBuilder import com.airbnb.deeplinkdispatch.DeepLink import com.airbnb.deeplinkdispatch.DeepLinkMethodResult // Method-level annotation — return Intent (Kotlin object) object ProductDeepLinks { @DeepLink("dld://methodDeepLink/{param1}") @JvmStatic fun intentForDeepLinkMethod(context: Context): Intent = Intent(context, MainActivity::class.java) .setAction("deep_link_method") // With Bundle access for query parameters @DeepLink("http://example.com/deepLink/{id}/{name}/{place}") @JvmStatic fun intentForTaskStackBuilderMethods(context: Context, bundle: Bundle?): TaskStackBuilder { val detailsIntent = Intent(context, SecondActivity::class.java) val parentIntent = Intent(context, MainActivity::class.java) return TaskStackBuilder.create(context).apply { addNextIntent(parentIntent) addNextIntent(detailsIntent) } } // Return DeepLinkMethodResult to choose between Intent and TaskStackBuilder at runtime @DeepLink("dld://host/methodResult/intent") @JvmStatic fun intentViaDeeplinkMethodResult(context: Context?): DeepLinkMethodResult = DeepLinkMethodResult( intent = Intent(context, SecondActivity::class.java), taskStackBuilder = null ) } ``` -------------------------------- ### Activity-level DeepLink Annotation (Kotlin) Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Annotates an Activity to handle specific deep link URIs. When activityClassFqn is set and the Gradle plugin is applied, an intent-filter entry is generated automatically in AndroidManifest.xml. Access deep link parameters and the referrer URI via the intent extras. ```kotlin import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.airbnb.deeplinkdispatch.DeepLink // Activity-level annotation (Kotlin) @DeepLink( "dld://example.com/deepLink", "https://example.com/products/{id}", "https://www.example.com//detail", activityClassFqn = "com.example.DeepLinkActivity" // enables manifest generation ) class ProductActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val intent = intent if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) { val params = intent.extras val productId = params?.getString("id") // path param val referrer = params?.getString(DeepLink.REFERRER_URI) // DeepLink.URI contains the original URI string } } } ``` -------------------------------- ### Implementing Type-Safe Handlers with DeepLinkHandler Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Implement the `DeepLinkHandler` interface to create type-safe objects that handle deep links. DeepLinkDispatch will automatically construct and pass a typed arguments object to your `handleDeepLink` method. ```APIDOC ## `DeepLinkHandler` interface — Type-safe handler objects Implement this interface in a Kotlin `object` (or class) and annotate it with `@DeepLink` or a custom spec annotation. DeepLinkDispatch calls `handleDeepLink` with a fully typed, auto-constructed args object instead of firing an `Intent`. Path parameters are non-nullable; query parameters are nullable. ```kotlin // 1. Define the args data class — every constructor parameter must be annotated data class ProductHandlerArgs( @DeeplinkParam("id", DeepLinkParamType.Path) val id: Long, @DeeplinkParam("slug", DeepLinkParamType.Path) val slug: String, @DeeplinkParam("ref", DeepLinkParamType.Query) val referrer: String?, @DeeplinkParam("preview", DeepLinkParamType.Query) val preview: Boolean?, ) // 2. Implement the handler @DeepLink( "https://example.com/products/{id}/{slug}", activityClassFqn = "com.example.DeepLinkActivity" ) object ProductDeepLinkHandler : DeepLinkHandler { override fun handleDeepLink(context: Context, deepLinkArgs: ProductHandlerArgs) { // Fully typed — no casting, no bundle key strings val intent = Intent(context, ProductActivity::class.java).apply { putExtra("product_id", deepLinkArgs.id) putExtra("product_slug", deepLinkArgs.slug) deepLinkArgs.referrer?.let { putExtra("referrer", it) } } context.startActivity(intent) } } // Java implementation @WebDeepLink({"/java/{seg1}/{seg2}?show_taxes={show_taxes}"}) public class JavaProductHandler implements DeepLinkHandler { @Override public void handleDeepLink(@NonNull Context context, JavaProductHandlerArgs args) { Intent intent = new Intent(context, ProductActivity.class); intent.putExtra("seg1", args.getSeg1()); context.startActivity(intent); } } // Inheritance chains are supported: abstract class BaseHandler : DeepLinkHandler { override fun handleDeepLink(context: Context, deepLinkArgs: T) { /* shared logic */ } } @DeepLink("dld://host/path") object ConcreteHandler : BaseHandler() ``` ``` -------------------------------- ### Apply Manifest Generation Gradle Plugin Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Apply the DeepLinkDispatch manifest generation plugin to library modules using KSP. This plugin automatically injects intent-filters into the module's MERGED_MANIFEST artifact. ```groovy // root build.gradle buildscript { dependencies { classpath "com.airbnb:deeplinkdispatch-gradle-plugin:7.3.0-SNAPSHOT" } } ``` ```groovy // library-module/build.gradle plugins { id("com.google.devtools.ksp") // Must be applied AFTER kotlin and android plugins, NOT on application modules } apply plugin: 'com.airbnb.deeplinkdispatch.manifest-generation' dependencies { implementation "com.airbnb:deeplinkdispatch:7.3.0-SNAPSHOT" ksp "com.airbnb:deeplinkdispatch-processor:7.3.0-SNAPSHOT" } ``` -------------------------------- ### Define Custom AppDeepLink Annotation Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Use this annotation to prefix all app deep link URIs with a common scheme and host. Ensure RetentionPolicy is RUNTIME when using tools like Dexguard. ```java // Prefix all app deep link URIs with "app://airbnb" @DeepLinkSpec(prefix = { "app://airbnb" }) // When using tools like Dexguard we require these annotations to still be inside the .dex files // produced by D8 but because of this bug https://issuetracker.google.com/issues/168524920 they // are not so we need to mark them as RetentionPolicy.RUNTIME. @Retention(RetentionPolicy.RUNTIME) public @interface AppDeepLink { String[] value(); } ``` -------------------------------- ### Create TaskStackBuilder for Custom Backstack Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Return a TaskStackBuilder instead of an Intent to customize the Activity backstack. DeepLinkDispatch will use the last Intent added to the builder. ```java @DeepLink("http://example.com/deepLink/{id}/{name}") public static TaskStackBuilder intentForTaskStackBuilderMethods(Context context) { Intent detailsIntent = new Intent(context, SecondActivity.class).setAction(ACTION_DEEP_LINK_COMPLEX); Intent parentIntent = new Intent(context, MainActivity.class).setAction(ACTION_DEEP_LINK_COMPLEX); TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context); taskStackBuilder.addNextIntent(parentIntent); taskStackBuilder.addNextIntent(detailsIntent); return taskStackBuilder; } ``` -------------------------------- ### Custom Type Conversion in Java Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md Implement custom type converters for DeepLinkDispatch by adding them to a TypeConverters instance and providing a lambda to the DeepLinkDelegate constructor. ```java import com.airbnb.deeplinkdispatch.DeepLinkDelegate; import com.airbnb.deeplinkdispatch.TypeConverters; import java.util.function.Function0; TypeConverters typeConverters = new TypeConverters(); typeConverters.put(ColorDrawable.class, value -> { switch (value.toLowerCase()) { case "red": return new ColorDrawable(0xff0000ff); } }); Function0 typeConvertersLambda = () -> typeConverters; DeepLinkDelegate deepLinkDelegate = new DeepLinkDelegate( ... typeConvertersLambda, ...); ``` -------------------------------- ### Register Custom Type Converters Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Implement `TypeConverter` to handle custom types like `ColorDrawable` and parameterized types such as `List`. Provide these converters to `DeepLinkDelegate` via a lambda for runtime updates. ```kotlin val colorConverter = TypeConverter { value -> when (value.lowercase()) { "red" -> ColorDrawable(Color.RED) "green" -> ColorDrawable(Color.GREEN) else -> ColorDrawable(Color.TRANSPARENT) } } val listConverter = TypeConverter> { value -> value.split(",").map { it.trim() } } val typeConverters = TypeConverters().apply { put(ColorDrawable::class.java, colorConverter) put(object : TypeToken>(){}.type, listConverter) } val deepLinkDelegate = DeepLinkDelegate( listOf(SampleModuleRegistry()), typeConverters = { typeConverters } ) data class TypedArgs( @DeeplinkParam("number", DeepLinkParamType.Path) val number: Long, @DeeplinkParam("color", DeepLinkParamType.Path) val color: ColorDrawable, @DeeplinkParam("tags", DeepLinkParamType.Path) val tags: List, ) // URL: http://testing.com/typeConversion/42/red // → TypedArgs(number=42, color=ColorDrawable(RED)) ``` -------------------------------- ### Configure Activity without Manifest Generation Source: https://github.com/airbnb/deeplinkdispatch/blob/master/README.md If not using manifest generation, manually add intent filters for your deep links to the Activity definition in AndroidManifest.xml. ```xml ``` -------------------------------- ### Observe Deep Link Dispatches with BroadcastReceiver Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Register a `BroadcastReceiver` to listen for local broadcasts sent by `DeepLinkHandler` after each dispatch. This allows monitoring of successes and failures. ```java // Application.java public class SampleApplication extends Application { @Override public void onCreate() { super.onCreate(); IntentFilter filter = new IntentFilter(DeepLinkHandler.ACTION); LocalBroadcastManager.getInstance(this) .registerReceiver(new DeepLinkReceiver(), filter); } } // DeepLinkReceiver.java public class DeepLinkReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String uri = intent.getStringExtra(DeepLinkHandler.EXTRA_URI); String template = intent.getStringExtra(DeepLinkHandler.EXTRA_URI_TEMPLATE); boolean ok = intent.getBooleanExtra(DeepLinkHandler.EXTRA_SUCCESSFUL, false); if (ok) { Log.i("DLD", "Deep link success: " + uri + " → " + template); } else { String error = intent.getStringExtra(DeepLinkHandler.EXTRA_ERROR_MESSAGE); Log.e("DLD", "Deep link error: " + uri + " — " + error); } } } ``` -------------------------------- ### Implement Custom Error Handler Source: https://context7.com/airbnb/deeplinkdispatch/llms.txt Extend `ErrorHandler` to intercept duplicate URI matches and type resolution failures. This prevents app crashes and allows for custom logging or reporting. ```kotlin val errorHandler = object : ErrorHandler() { override fun duplicateMatch( uriString: String, duplicatedMatches: List ) { // Log or report: two registries claim the same URI with equal concreteness analytics.logEvent("deeplink_duplicate", mapOf( "uri" to uriString, "matches" to duplicatedMatches.map { it.deeplinkEntry.uriTemplate } )) } override fun unableToDetermineHandlerArgsType( uriTemplate: String, className: String ) { crashReporter.logError("Cannot resolve DeepLinkHandler args for $className ($uriTemplate)") } } val deepLinkDelegate = DeepLinkDelegate( listOf(AppModuleRegistry()), errorHandler = errorHandler ) ```