Skip to content
87 changes: 75 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,25 @@ Open Android Studio, and open the `android-sdk-example` folder in Android Studio

## 🛠 Setup Instructions

### 1. Add Your API Key

Open the Main Activity file and replace the placeholder with your Short.io Public API Key:
### Initialize the SDK

```bash
val apiKey = "your_api_key"
To start using ShortioSdk, you need to initialize it early in your app lifecycle, preferably in your Activity's onCreate() method or in your custom Application class.

Example: Initialize in Activity

```kotlin
override fun onCreate() {
super.onCreate()
ShortioSdk.initialize(apiKey, domain) ////Replace with your Short.io API KEY and Domain in Constants File
}
```
* apiKey: Your API key string for initialization.
* domain: The default domain to use for URL shortening.





### 🔗 Need help finding your API key?

Expand All @@ -46,7 +58,6 @@ In your MainActivity file replace the placeholder with your Short.io domain and

```kotlin
val params = ShortIOParameters(
domain = "your_domain", // e.g., example.short.gy
originalURL = "https://{your_domain}" // The destination URL
)
```
Expand All @@ -60,14 +71,12 @@ The app demonstrates:
Using your domain and original URL, you can generate a short link like this:

```kotlin
val apiKey = "your_api_key"

val params = ShortIOParameters(
domain = "your_domain", // e.g., example.short.gy
originalURL = "https://{your_domain}" // The destination URL
)

when (val result = ShortioSdk.shortenUrl(apiKey, params)) {
when (val result = ShortioSdk.createShortLink(params)) {
is ShortIOResult.Success -> {
val shortUrl = result.data.shortURL
Log.d("ShortIO", "Shortened URL: $shortUrl")
Expand All @@ -78,6 +87,40 @@ when (val result = ShortioSdk.shortenUrl(apiKey, params)) {
}
}
```
**Note**: Only the `originalURL` is the required parameter as `domain` is passed in the initialize method of SDK. You can also pass optional parameters such as `path`, `title`, `utmParameters`, etc.

### 🔐 Secure Short Link

If you want to encrypt the original URL, the SDK provides a `createSecure` function that uses AES-GCM encryption.

```kotlin
val originalURL = "your_original_URL"
val result = ShortioSdk.createSecure(originalURL)
Log.d("SecureURL", "RESULT: ${result}")
Log.d("securedOriginalURL", "URL: ${result.securedOriginalURL}")
Log.d("securedShortUrl", "URL: ${result.securedShortUrl}")
```

### 🔄 Conversion Tracking

Track conversions for your short links to measure campaign effectiveness. The SDK provides a simple method to record conversions.

```kotlin
CoroutineScope(Dispatchers.IO).launch {
try {
val res = ShortioSdk.trackConversion(
domain: "https://{your_domain}", // ⚠️ Deprecated (optional):
clid: "your_clid", // ⚠️ Deprecated (optional):
conversionId: "your_conversionID" // (optional)
)
// conversionId can be 'signup', 'purchase', 'download', etc.
Log.d("Handle Conversion Tracking", "Handle Conversion Tracking: $res")
} catch (e: Exception) {
Log.e("Handle Conversion Tracking", "Error calling trackConversion", e)
}
}
```

## 🤖 Deep Linking Setup
To handle deep links via Short.io on Android, you'll need to set up Android App Links properly using your domain's Digital Asset Links and intent filters.

Expand Down Expand Up @@ -153,25 +196,45 @@ keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -sto

### 🧭 Step 5: Handle Incoming URLs with onNewIntent() Method

To retrieve the original URL from Short.io links in your Android app, you can handle incoming intents in onNewIntent(), which allows your activity to process links that are opened while it is already running.

1. Open your main activity file (e.g., MainActivity.kt).

2. Override the onNewIntent() method to receive new intents when the activity is already running:

```kotlin
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val result = ShortioSdk.handleIntent(intent)
Log.d("New Intent", "Host: ${result?.host}, Path: ${result?.path}")
lifecycleScope.launch {
val result = ShortioSdk.handleIntent(intent)
// Access the original URL
val originalUrl = result?.destinationUrl
Log.d("New Intent",
"Host: ${result?.host},
Path: ${result?.path},
Original URL: $originalUrl"
)
}
}
```
3. In the same activity, you can also handle the initial intent inside the `onCreate()` method:

```kotlin
// Optional
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
val result = ShortioSdk.handleIntent(intent)
Log.d("New Intent", "Host: ${result?.host}, Path: ${result?.path}")
// Access the original URL
val originalUrl = result?.destinationUrl
Log.d("New Intent",
"Host: ${result?.host},
Path: ${result?.path},
Original URL: $originalUrl"
)
}
}
```

Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation("com.github.Short-io:android-sdk:v1.0.5")
implementation("com.github.Short-io:android-sdk:v1.0.9")
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/com/github/shortioapp/Constants.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.github.shortioapp


//Replace with your Short.io API KEY
val apiKey = "YOUR_API_KEY"

//Replace with your Short.io domain
val domain = "YOUR_DOMAIN"
152 changes: 143 additions & 9 deletions app/src/main/java/com/github/shortioapp/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,19 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ShortioSdk.initialize( apiKey, domain) //Replace with your Short.io API KEY and Domain in Constants File
enableEdgeToEdge()

val apiKey = "your_api_key"

setContent {
ShortIOAppTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Expand All @@ -58,7 +62,13 @@ class MainActivity : ComponentActivity() {


Spacer(modifier = Modifier.height(16.dp))
LinkShorteningButton(apiKey = apiKey)
LinkShorteningButton()

Spacer(modifier = Modifier.height(16.dp))
CreateSecureUrlButton()

Spacer(modifier = Modifier.height(16.dp))
TrackConversionButton()
}
}
}
Expand All @@ -67,13 +77,15 @@ class MainActivity : ComponentActivity() {

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val response = ShortioSdk.handleIntent(intent)
Log.d("New Intent", "Host: ${response?.host}, Path: ${response?.path}")
lifecycleScope.launch {
val result = ShortioSdk.handleIntent(intent)
Log.d("New Intent", "Host: ${result?.host}, Path: ${result?.path}, DestinationURL: ${result?.destinationUrl}")
}
}
}

@Composable
fun LinkShorteningButton(apiKey: String) {
fun LinkShorteningButton() {
var isLoading by remember { mutableStateOf(false) }
var resultMessage by remember { mutableStateOf<String?>(null) }
var isError by remember { mutableStateOf(false) }
Expand All @@ -88,11 +100,10 @@ fun LinkShorteningButton(apiKey: String) {
thread {
try {
val params = ShortIOParameters(
originalURL = "https://{your_domain}",
domain = "your_domain"
originalURL = "https://{YOUR_DOMAIN}/",
)

when (val result = ShortioSdk.shortenUrl(apiKey, params)) {
when (val result = ShortioSdk.createShortLink(params)) {
is ShortIOResult.Success -> {
val shortUrl = result.data.shortURL
Log.d("ShortIO", "Shortened URL: $shortUrl")
Expand Down Expand Up @@ -171,6 +182,129 @@ fun LinkShorteningButton(apiKey: String) {
}
}

@Composable
fun CreateSecureUrlButton() {
var resultMessage by remember { mutableStateOf<String?>(null) }
var isLoading by remember { mutableStateOf(false) }
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()

Column(horizontalAlignment = Alignment.CenterHorizontally) {
Button(
onClick = {
isLoading = true
coroutineScope.launch {
try {
val originalUrl = "https://{your_domain}"
val result = ShortioSdk.createSecure(originalUrl)

Log.d("SecureURL", "RESULT: $result")
Log.d("SecureURL", "URL: ${result.securedOriginalURL}")
Log.d("SecureURL", "KEY: ${result.securedShortUrl}")

(context as ComponentActivity).runOnUiThread {
isLoading = false
resultMessage = "Secure URL: ${result.securedShortUrl}"
}
} catch (e: Exception) {
Log.e("SecureURL", "Exception: ${e.message}", e)
(context as ComponentActivity).runOnUiThread {
isLoading = false
resultMessage = "Error: ${e.message}"
}
}
}
},
enabled = !isLoading
) {
Text(text = if (isLoading) "Generating..." else "Create Secure Short Link")
}

if (isLoading) {
Spacer(modifier = Modifier.height(16.dp))
CircularProgressIndicator()
}

resultMessage?.let {
Spacer(modifier = Modifier.height(16.dp))
if (it.startsWith("Error")) {
Text(
text = it,
fontSize = 16.sp,
color = Color.Red,
modifier = Modifier.padding(horizontal = 8.dp)
)
} else {
val secureUrl = it.substringAfter("Secure URL:").trim()

Text(
text = secureUrl,
fontSize = 16.sp,
color = Color(0xFF4CAF50),
modifier = Modifier.padding(horizontal = 8.dp)
)

Spacer(modifier = Modifier.height(8.dp))

Button(
onClick = {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("secureURL", secureUrl)
clipboard.setPrimaryClip(clip)
Toast.makeText(context, "Secure URL copied to clipboard", Toast.LENGTH_SHORT).show()
}
) {
Text("Copy Secure URL")
}
}
}
}
}

@Composable
fun TrackConversionButton() {
var conversionResult by remember { mutableStateOf<Boolean?>(null) }
var errorMessage by remember { mutableStateOf<String?>(null) }

Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(
onClick = {
CoroutineScope(Dispatchers.IO).launch {
try {
val res = ShortioSdk.trackConversion(
"your_clid", // ⚠️ Deprecated (optional)
"your_domain", // ⚠️ Deprecated (optional)
"your_conversionID" //(optional)
)
conversionResult = res
errorMessage = null
} catch (e: Exception) {
conversionResult = null
errorMessage = e.message
Log.e("Handle Conversion Tracking", "Error calling trackConversion", e)
}
}
}
) {
Text(text = "Conversion Tracking")
}
conversionResult?.let { success ->
Text(
text = if (success) "Conversion successful" else "Conversion failed",
color = if (success) Color(0xFF4CAF50) else Color.Red
)
}
errorMessage?.let {
Text(
text = it,
color = Color.Red
)
}
}
}

@Composable
fun LinkShortnerTitle() {
Expand Down