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

ItemValue
compileSdk33
minSdk24
Java bytecode8

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

OptionDefaultDescription
API keyrequiredDedicated SDK credential; assume values shipped in the app can be extracted
LoggingfalseSDK diagnostic logging; enable only when needed
Deferred deep linktrueCheck the Play Install Referrer on first launch
Base URLLimeLink serviceOptional 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.

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:

  1. Add an App Link <data> entry for the exact custom hostname used by the Project.
  2. Serve a valid Digital Asset Links file from https://YOUR_CUSTOM_DOMAIN/.well-known/assetlinks.json.
  3. Include the Android application ID and signing-certificate fingerprint for the app build being tested.
  4. 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.

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

FieldDescription
originalUrlOriginal LimeLink URL, when available
resolvedUriDestination URI resolved by LimeLink
queryParamsQuery parameters from the original URL
pathParamsParsed mainPath and optional subPath
isDeferredtrue for an install-time deferred result

Use the facade when your Activity needs to forward an Intent explicitly:

LimeLinkSDK.handleUniversalLink(this, intent)

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.
    }
}

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

APIPurpose
LimeLinkSDK.init(app, config)Initialize once
LimeLinkSDK.isInitializedRead 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

APIPurpose
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

Troubleshooting

Gradle cannot resolve the package

  1. Confirm LimeLinkSDK.init() runs once.
  2. Confirm the listener remains registered.
  3. Verify the manifest intent filter and singleTop launch mode.
  4. Verify the domain's Digital Asset Links configuration.
  5. Enable SDK logging in a debug build.
  1. Confirm deferred handling is enabled.
  2. Install through Google Play; sideloaded builds do not receive Play Install Referrer data.
  3. Verify the backend-generated referrer value has not expired.
  4. Treat a normal no-result as a valid outcome.