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>

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>

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

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.

SDK Usage Guide

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>

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
        }
    }
}
  1. User clicks a URL in the format https://{suffix}.limelink.org/link/{link_suffix}?test=1&value=2
  2. SDK extracts the {suffix} and {link_suffix} parts from the URL
  3. SDK extracts the full original URL (including query parameters) as full_request_url
  4. SDK extracts all query parameters from the original URL
  5. Fetches headers from https://{suffix}.limelink.org for additional context
  6. Calls the API https://www.limelink.org/api/v1/app/dynamic_link/{link_suffix}?full_request_url={full_request_url}&test=1&value=2 with headers, full_request_url, and all query parameters
  7. Receives uri from API response and automatically redirects to that URL
  8. If link_suffix is not found or uri is missing, returns 404 error

For backward compatibility, the SDK also supports the legacy deeplink format:

  1. User clicks a URL in the format https://deep.limelink.org/link/subdomain={subdomain}&path={path}&platform=android
  2. SDK calls the API https://deep.limelink.org/link with query parameters
  3. Receives deeplinkUrl from 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)
    }
}

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

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 is useful for:

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.

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
    }
}
  1. User clicks a link before installing the app
  2. Link redirects to app store (Play Store or App Store) with code={suffix}&full_request_url={full_request_url} parameters
  3. User installs and opens the app
  4. App retrieves the suffix and full_request_url from Install Referrer API (code={suffix}&full_request_url={full_request_url})
  5. App calls handleDeferredDeepLink(suffix, fullRequestUrl) with the suffix and full_request_url
  6. SDK calls /api/v1/app/dynamic_link/{suffix}?full_request_url={full_request_url}&event_type=setup
  7. SDK returns URI from API response
  8. 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:

  1. Add dependency to your build.gradle:
dependencies {
    implementation 'com.android.installreferrer:installreferrer:2.2'
}
  1. 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