LimeLink Android SDK
This is a dedicated limelink dynamic link library. The limelink SDK is used to save statistics related to the first run or relaunch of the app, or to control handle values specified for each dynamic link through the https://limelink.org console.
Getting Started
This section guides you on how to set up and run this project locally.
Installation and requirements
Add the following items to the AndroidManifest.xml file
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<!--Example-->
<uses-permission android:name="android.permission.INTERNET" />
<!--Example-->
<application
android:networkSecurityConfig="@xml/network_security_config">
</application>
</manifest>
- Internet Permission: To call external APIs, you need to declare the INTERNET permission in the AndroidManifest.xml file.
- Network Security Configuration: Starting from API 28 (Android 9.0 Pie), Cleartext (HTTP) is blocked by default. Explicitly configure the network security settings.
Create the res/xml/network_security_config.xml file:
<?xml version="1.0" encoding="utf-8"?>
<!--Example-->
<network-security-config>
<!-- Default configuration: Allow cleartext traffic for all domains -->
<base-config cleartextTrafficPermitted="true" />
<!-- Domain-specific configuration: Optional -->
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">limelink.org</domain>
</domain-config>
</network-security-config>
- In this configuration, base-config allows unencrypted traffic for all domains, whereas domain-config is set to permit HTTPS only for a specific domain (limelink.org).
By adhering to the above guidelines, Android developers should face minimal issues when integrating and using an SDK for calling external APIs.
Step 1: Add the JitPack repository to your build file
Add it in your root build.gradle at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Step 2: Add the dependency
dependencies {
implementation 'com.github.hellovelope:limelink-aos-sdk:main'
}
- Please refer to *here for maven, sbt, or leiningen.
Step 3: Manifest file configuration
In the AndroidManifest.xml file, add an intent filter to the MainActivity to handle URLs like schem://example
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:networkSecurityConfig="@xml/network_security_config">
<!--Example-->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!--Please enter the domain address you want.-->
<data android:scheme="https" android:host="customdomain.com"/>
</intent-filter>
</activity>
</application>
</manifest>
If it's completed, let's refer to the SDK Usage Guide and create it.
- For more details, please refer to the official document. -> *official link
SDK Usage Guide
Universal Link Handling
Universal Link Setup
Add the following configuration to your AndroidManifest.xml to handle Universal Links:
<activity android:name=".MainActivity">
<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"/>
</intent-filter>
</activity>
Universal Link Usage
How to handle Universal Links in MainActivity:
package com.example.myapp
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.limelink.limelink_aos_sdk.LimeLinkSDK
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Handle Universal Link in onCreate
handleIntent(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// Handle Universal Link in onNewIntent (when app is already running)
intent?.let { handleIntent(it) }
}
private fun handleIntent(intent: Intent) {
// Check if it's a Universal Link and handle it
if (LimeLinkSDK.isUniversalLink(intent)) {
LimeLinkSDK.handleUniversalLink(this, intent) { uri ->
if (uri != null) {
// Handle success - you can now use the URI as needed
println("Universal Link URI: $uri")
// Example: Navigate to the URI
// navigateToUri(uri)
// Example: Open in browser
// openInBrowser(uri)
// Example: Parse and handle internally
// handleInternalNavigation(uri)
} else {
// Handle failure
println("Universal Link handling failed")
}
}
} else {
// Handle regular intent
handleCustomScheme(intent)
}
}
private fun handleCustomScheme(intent: Intent) {
// Handle existing custom scheme
val originalUrl = LimeLinkSDK.getSchemeFromIntent(intent)
originalUrl?.let {
// URL processing logic
}
}
}
Universal Link Flow
- User clicks a URL in the format
https://{suffix}.limelink.org/link/{link_suffix}?test=1&value=2 - SDK extracts the
{suffix}and{link_suffix}parts from the URL - SDK extracts the full original URL (including query parameters) as
full_request_url - SDK extracts all query parameters from the original URL
- Fetches headers from
https://{suffix}.limelink.orgfor additional context - Calls the API
https://www.limelink.org/api/v1/app/dynamic_link/{link_suffix}?full_request_url={full_request_url}&test=1&value=2with headers, full_request_url, and all query parameters - Receives
urifrom API response and automatically redirects to that URL - If link_suffix is not found or uri is missing, returns 404 error
Legacy Deeplink Support
For backward compatibility, the SDK also supports the legacy deeplink format:
- User clicks a URL in the format
https://deep.limelink.org/link/subdomain={subdomain}&path={path}&platform=android - SDK calls the API
https://deep.limelink.org/linkwith query parameters - Receives
deeplinkUrlfrom API response and redirects accordingly
Save statistical information
Open MainActivity.kt and add the following code
package com.example.myapp
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.limelink.limelink_aos_sdk.LinkStats
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/*Example*/
val privateKey = "your_private_key"
saveLimeLinkStatus(this, intent, privateKey)
}
}
- This way, you can save information about the first run or relaunch of the app. You can check the actual metrics on the https://limelink.org console.
- The privateKey value is required. If you don't have it, obtain it from the https://limelink.org console and use it.
Use handle information superficially
Open MainActivity.kt and add the following code
package com.example.myapp
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.limelink.limelink_aos_sdk.response.PathParamResponse
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
handleIntent(intent)
}
private fun handleIntent(intent: Intent) {
/*Example*/
val pathParamResponse: PathParamResponse = UrlHandler.parsePathParams(intent)
val suffix: String = pathParamResponse.getMainPath()
/*Use the handle values to navigate to the desired screen. */
val handle: String = pathParamResponse.getSubPath()
if (handle == "example") {
//Navigate to the desired screen
}
}
}
- This way, you can handle the information superficially and navigate to the desired screen based on the handle value.
Deferred Deep Link
Deferred Deep Link allows you to track and handle deep links even when the app is not installed. When a user clicks a link before installing the app, the link information is stored and can be retrieved after the app is installed and launched for the first time.
Deferred Deep Link Overview
Deferred Deep Link is useful for:
- Tracking user acquisition sources
- Providing personalized onboarding experiences
- Redirecting users to specific content after app installation
- Measuring marketing campaign effectiveness
Note: Deferred deep links are created and managed through the LimeLink Console. The SDK provides functionality to retrieve deferred deep link information when the app is launched for the first time after installation using Install Referrer API.
Deferred Deep Link Usage
Use this when the app is launched for the first time after installation. The SDK calls /api/v1/app/dynamic_link/{suffix}?full_request_url={full_request_url}&event_type=setup and returns the URI for redirection.
import org.limelink.limelink_aos_sdk.LimeLinkSDK
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Check if this is the first launch
if (isFirstLaunch()) {
// Get the suffix and full_request_url from Install Referrer API
// Install Referrer provides code={suffix}&full_request_url={full_request_url} parameters
val (suffix, fullRequestUrl) = getDeferredDeepLinkParams() // You need to implement this
if (suffix != null) {
// Handle deferred deep link
// API will be called as: /api/v1/app/dynamic_link/{suffix}?full_request_url={full_request_url}&event_type=setup
LimeLinkSDK.handleDeferredDeepLink(
suffix = suffix,
fullRequestUrl = fullRequestUrl
) { uri ->
if (uri != null) {
// Handle success - you can now use the URI as needed
println("Deferred Deep Link URI: $uri")
// Example: Navigate to the URI
// navigateToUri(uri)
// Example: Open in browser
// openInBrowser(uri)
// Example: Parse and handle internally
// handleInternalNavigation(uri)
} else {
// Handle failure
println("Deferred Deep Link handling failed")
// Proceed with normal app launch
navigateToDefault()
}
}
} else {
// No suffix found in install referrer
navigateToDefault()
}
} else {
// Normal app launch
navigateToDefault()
}
}
private fun isFirstLaunch(): Boolean {
// Implement your first launch detection logic
// You can use SharedPreferences or other methods
val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
val isFirst = prefs.getBoolean("is_first_launch", true)
if (isFirst) {
prefs.edit().putBoolean("is_first_launch", false).apply()
}
return isFirst
}
private fun getDeferredDeepLinkParams(): Pair<String?, String?> {
// Implement logic to get suffix and full_request_url from Install Referrer API
// Install Referrer provides code={suffix}&full_request_url={full_request_url} parameters
// Example using Play Install Referrer Library:
/*
val referrerClient = InstallReferrerClient.newBuilder(this).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
val response = referrerClient.installReferrer
val referrerUrl = response.installReferrer
// Parse code={suffix} and full_request_url={full_request_url} from referrerUrl
// Example: "code=abc123&full_request_url=https://example.com" -> extract "abc123" and "https://example.com"
// Return Pair(suffix, fullRequestUrl)
}
}
}
override fun onInstallReferrerServiceDisconnected() {}
})
*/
return Pair(null, null) // Placeholder - implement your logic
}
}
Deferred Deep Link Flow
- User clicks a link before installing the app
- Link redirects to app store (Play Store or App Store) with
code={suffix}&full_request_url={full_request_url}parameters - User installs and opens the app
- App retrieves the suffix and full_request_url from Install Referrer API (
code={suffix}&full_request_url={full_request_url}) - App calls
handleDeferredDeepLink(suffix, fullRequestUrl)with the suffix and full_request_url - SDK calls
/api/v1/app/dynamic_link/{suffix}?full_request_url={full_request_url}&event_type=setup - SDK returns URI from API response
- App navigates to the URI or handles it as needed
Install Referrer Setup
To use Deferred Deep Link, you need to integrate the Play Install Referrer Library:
- Add dependency to your
build.gradle:
dependencies {
implementation 'com.android.installreferrer:installreferrer:2.2'
}
- Get the suffix and full_request_url from Install Referrer:
import com.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener
val referrerClient = InstallReferrerClient.newBuilder(context).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
val response = referrerClient.installReferrer
val referrerUrl = response.installReferrer
// Parse code={suffix} and full_request_url={full_request_url} from referrerUrl
// Example: "code=abc123&full_request_url=https://example.com" -> extract "abc123" and "https://example.com"
val (suffix, fullRequestUrl) = extractParamsFromReferrer(referrerUrl)
suffix?.let {
LimeLinkSDK.handleDeferredDeepLink(
suffix = it,
fullRequestUrl = fullRequestUrl
) { uri ->
// Handle the URI
}
}
}
}
referrerClient.endConnection()
}
override fun onInstallReferrerServiceDisconnected() {
// Handle disconnection
}
})
private fun extractParamsFromReferrer(referrerUrl: String): Pair<String?, String?> {
// Parse code={suffix} and full_request_url={full_request_url} parameters from referrer URL
val params = referrerUrl.split("&")
var suffix: String? = null
var fullRequestUrl: String? = null
for (param in params) {
when {
param.startsWith("code=") -> {
suffix = param.substring(5) // Remove "code=" prefix
}
param.startsWith("full_request_url=") -> {
fullRequestUrl = param.substring(17) // Remove "full_request_url=" prefix
}
}
}
return Pair(suffix, fullRequestUrl)
}
Best Practices
- First Launch Detection: Properly detect first app launch to avoid unnecessary API calls
- Error Handling: Always handle the case where suffix is not found or API call fails
- Install Referrer: Use Play Install Referrer Library for reliable referrer information
- URI Handling: Validate and sanitize the received URI before using it
- Fallback: Always provide a fallback navigation path when deferred deep link is not available