LimeLink Android SDK
Target SDK Version: 0.3.2 | Min SDK: 24 | Compile SDK: 33 | Java bytecode: 8
LimeLink Android SDK provides App Link resolution, deferred deep links, and link event tracking through a single public facade.
View previous Android SDK releases
Requirements
SDK Release Values
| Item | Value |
|---|---|
| compileSdk | 33 |
| minSdk | 24 |
| Java bytecode | 8 |
Verified Legacy Consumer Baseline
Version 0.3.2 has been consumer-verified with Gradle 7.3, AGP 7.0.4, JDK 11, and Kotlin 1.6.10. These are verified legacy-consumer values, not requirements for building the SDK from source. Java-only consumers are supported.
Installation
Production SDK builds are distributed as verified, minified binaries through LimeLink's public Maven repository. No GitHub account, PAT, repository invitation, SSO approval, or Gradle credentials are required.
Add the public Maven repository to settings.gradle.kts:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url = uri("https://hellovelop.github.io/limelink-aos-sdk-binary/repository")
content {
includeGroup("org.limelink")
}
}
}
}
Add the dependency:
dependencies {
implementation("org.limelink:limelink-aos-sdk:0.3.2")
}
JitPack, source dependencies, Maven Local, debug artifacts, direct AAR downloads, and composite builds are not supported consumer distribution channels.
Initialize the SDK
Initialize the SDK once in your Application:
import android.app.Application
import org.limelink.limelink_aos_sdk.LimeLinkSDK
import org.limelink.limelink_aos_sdk.config.LimeLinkConfig
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
val config = LimeLinkConfig.Builder("YOUR_API_KEY")
.setLogging(BuildConfig.DEBUG)
.setDeferredDeeplinkEnabled(true)
.build()
LimeLinkSDK.init(this, config)
}
}
Register the application class in AndroidManifest.xml:
<application
android:name=".MyApp"
... />
Configuration
| Option | Default | Description |
|---|---|---|
| API key | required | Dedicated SDK credential; assume values shipped in the app can be extracted |
| Logging | false | SDK diagnostic logging; enable only when needed |
| Deferred deep link | true | Check the Play Install Referrer on first launch |
| Base URL | LimeLink service | Optional custom service URL |
The SDK API key is embedded in app configuration and can be extracted from a shipped application. Use a dedicated SDK credential that is not reused by backend automation, keep it out of source control and logs, monitor its use, and rotate it after suspected exposure.
Java Integration
Java consumers use the static LimeLinkJavaSDK facade. Initialize it once from the Android main thread; calling initialization from another thread throws IllegalStateException rather than blocking.
LimeLinkJavaSDK.initialize(getApplicationContext(), config, listener);
LimeLinkJavaSDK.handleUniversalLink(this, getIntent());
LimeLinkJavaSDK.handleDeferredDeepLink(
getApplicationContext(),
new DeferredDeepLinkCallback() {
@Override
public void onMatched(LimeLinkResult result) {
// Navigate using the matched result.
}
@Override
public void onNotFound() {
// Normal outcome: continue the default launch flow.
}
@Override
public void onFailed(LimeLinkError error) {
// Report a safe public error without logging credentials.
}
}
);
boolean initialized = LimeLinkJavaSDK.isInitialized();
onMatched, onNotFound, and onFailed are distinct terminal outcomes. Callback and listener delivery occur exactly once on the Android main thread. When both receive a manual terminal outcome, their relative order is not guaranteed. The Java facade does not expose Kotlin function, coroutine, or Continuation types.
Configure Android App Links
Add Internet permission and an App Link intent filter:
<uses-permission android:name="android.permission.INTERNET" />
<application android:name=".MyApp">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop">
<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="*.limelink.org"
android:pathPrefix="/link/" />
</intent-filter>
</activity>
</application>
Custom Domains
Custom Domains require an active Pro Project. For each custom hostname:
- Add an App Link
<data>entry for the exact custom hostname used by the Project. - Serve a valid Digital Asset Links file from
https://YOUR_CUSTOM_DOMAIN/.well-known/assetlinks.json. - Include the Android application ID and signing-certificate fingerprint for the app build being tested.
- Verify domain association before testing a LimeLink URL on that hostname.
<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="links.example.com" />
</intent-filter>
Declare each custom hostname explicitly; the *.limelink.org entry does not associate an unrelated custom domain.
LimeLink endpoints use HTTPS. The SDK does not require global cleartext traffic, so do not enable cleartextTrafficPermitted="true" for this integration.
Receive Link Results
Register a long-lived listener after initialization:
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import org.limelink.limelink_aos_sdk.LimeLinkListener
import org.limelink.limelink_aos_sdk.LimeLinkSDK
import org.limelink.limelink_aos_sdk.response.LimeLinkError
import org.limelink.limelink_aos_sdk.response.LimeLinkResult
class MainActivity : ComponentActivity() {
private val linkListener = object : LimeLinkListener {
override fun onDeeplinkReceived(result: LimeLinkResult) {
val destination = result.resolvedUri
val isDeferred = result.isDeferred
val query = result.queryParams
// Route to destination in your app.
}
override fun onDeferredDeepLinkNotFound() {
// Normal outcome: continue the default launch flow.
}
override fun onDeeplinkError(error: LimeLinkError) {
Log.e("LimeLink", "[${error.code}] ${error.message}")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LimeLinkSDK.addLinkListener(linkListener)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
}
override fun onDestroy() {
LimeLinkSDK.removeLinkListener(linkListener)
super.onDestroy()
}
}
Listener callbacks and manual completions are delivered on the Android main thread. Lifecycle integration automatically handles normal Activity intents. If your host app also forwards an Intent explicitly, the SDK prevents the same Intent instance from being processed twice.
A Universal Link result completed before listener registration is retained in process memory and replayed once to the first listener. Check LimeLinkSDK.isInitialized when the host needs read-only initialization state.
Result fields
| Field | Description |
|---|---|
originalUrl | Original LimeLink URL, when available |
resolvedUri | Destination URI resolved by LimeLink |
queryParams | Query parameters from the original URL |
pathParams | Parsed mainPath and optional subPath |
isDeferred | true for an install-time deferred result |
Explicit Link Handling
Use the facade when your Activity needs to forward an Intent explicitly:
LimeLinkSDK.handleUniversalLink(this, intent)
Deferred Deep Links
For Android 0.3.2, the Play Store referrer contains a LimeLink-issued UUID. With deferred handling enabled, the SDK checks it automatically during initialization and resolves a valid result through the listener.
You can also request a fresh check explicitly:
LimeLinkSDK.handleDeferredDeepLink(this) { result, error ->
when {
result != null -> navigateTo(result.resolvedUri)
error != null -> Log.e("LimeLink", error.message)
else -> Unit // Normal no-result outcome.
}
}
matchedreturns a deferred result and callsonDeeplinkReceivedexactly once.notFoundreturns(null, null)and callsonDeferredDeepLinkNotFound()exactly once.failedreturns a safeLimeLinkErrorand callsonDeeplinkErrorexactly once.- A manual terminal outcome is delivered once to completion and once to the current listener; their relative order is not guaranteed.
- Automatic no-match is delivered once to a currently registered listener but is not replayed to a late listener.
- Explicit manual checks do not change automatic checked state.
- Public errors expose only
codeandmessage; raw referrer values, UUIDs, native causes, and sensitive diagnostics are not public.
Manual Stats Tracking
Link handling tracks the normal event automatically. If you need to track the current Intent explicitly:
LimeLinkSDK.trackLinkStatus(this, intent)
Public API
| API | Purpose |
|---|---|
LimeLinkSDK.init(app, config) | Initialize once |
LimeLinkSDK.isInitialized | Read current initialization state |
addLinkListener(listener) | Register result callbacks |
removeLinkListener(listener) | Remove callbacks |
handleUniversalLink(activity, intent) | Explicitly process an App Link |
handleDeferredDeepLink(context, completion?) | Explicitly check the deferred result |
trackLinkStatus(context, intent) | Explicitly send link stats |
Use the facade and public result/configuration models only. Install Referrer helpers, URL parsers, network services, request models, and SDK state are internal implementation details.
Java Facade
| API | Purpose |
|---|---|
LimeLinkJavaSDK.initialize(context, config, listener) | Initialize once on the main thread |
LimeLinkJavaSDK.handleUniversalLink(activity, intent) | Explicitly process an App Link |
LimeLinkJavaSDK.handleDeferredDeepLink(context, callback) | Explicitly check deferred resolution |
LimeLinkJavaSDK.isInitialized() | Read current initialization state |
Migrating to 0.3.2
- Replace private GitHub Packages, JitPack, or source installation with the credential-free public Maven repository.
- Update coordinates to
org.limelink:limelink-aos-sdk:0.3.2. - Implement
onDeferredDeepLinkNotFound()when the app needs an explicit normal no-match signal. - Keep one navigation owner when using both listener and manual completion callbacks.
- Use
LimeLinkSDK.isInitializedonly as read-only state. - Existing
0.3.0facade integrations remain source compatible. - Java consumers can use
LimeLinkJavaSDKandDeferredDeepLinkCallbackwithout Kotlin function types. - Call Java initialization on the Android main thread.
- Add an exact App Link hostname and Digital Asset Links file for every Custom Domain.
Troubleshooting
Gradle cannot resolve the package
- Confirm the public Maven repository URL is configured in
settings.gradle.kts. - Confirm the repository includes the
org.limelinkgroup. - Confirm the dependency uses the hyphenated artifact ID
limelink-aos-sdkand version0.3.2. - Refresh Gradle dependencies after changing repository configuration.
Links are not delivered
- Confirm
LimeLinkSDK.init()runs once. - Confirm the listener remains registered.
- Verify the manifest intent filter and
singleToplaunch mode. - Verify the domain's Digital Asset Links configuration.
- Enable SDK logging in a debug build.
Deferred links are not delivered
- Confirm deferred handling is enabled.
- Install through Google Play; sideloaded builds do not receive Play Install Referrer data.
- Verify the backend-generated referrer value has not expired.
- Treat a normal no-result as a valid outcome.