diff --git a/README.md b/README.md index d6e4ada..2a01f75 100644 --- a/README.md +++ b/README.md @@ -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? @@ -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 ) ``` @@ -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") @@ -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. @@ -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" + ) + } } ``` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 293b176..a5d35ad 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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) diff --git a/app/src/main/java/com/github/shortioapp/Constants.kt b/app/src/main/java/com/github/shortioapp/Constants.kt new file mode 100644 index 0000000..b224e11 --- /dev/null +++ b/app/src/main/java/com/github/shortioapp/Constants.kt @@ -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" \ No newline at end of file diff --git a/app/src/main/java/com/github/shortioapp/MainActivity.kt b/app/src/main/java/com/github/shortioapp/MainActivity.kt index 2261505..04908ff 100644 --- a/app/src/main/java/com/github/shortioapp/MainActivity.kt +++ b/app/src/main/java/com/github/shortioapp/MainActivity.kt @@ -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 -> @@ -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() } } } @@ -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(null) } var isError by remember { mutableStateOf(false) } @@ -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") @@ -171,6 +182,129 @@ fun LinkShorteningButton(apiKey: String) { } } +@Composable +fun CreateSecureUrlButton() { + var resultMessage by remember { mutableStateOf(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(null) } + var errorMessage by remember { mutableStateOf(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() {