Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI

on:
workflow_dispatch:
pull_request:
push:
branches: [main, short-io-app]

# Least privilege: nothing here needs more than reading the checkout.
permissions:
contents: read

# Cancel superseded PR runs; branch pushes finish so the badge never shows a cancellation.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
JAVA_VERSION: "17"

jobs:
# Compose tests run under Robolectric; they never click the two buttons that
# would call the live Short.io API.
build:
name: build
runs-on: ubuntu-latest
# Otherwise a wedged daemon runs to the 360-minute default.
timeout-minutes: 20
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false

- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}

- uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1

# Split out so a JitPack outage reads as a dependency failure, not a compile failure.
# Robolectric caches android-all jars outside ~/.gradle, so they need their own key.
- name: Cache Robolectric runtimes
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ~/.m2/repository/org/robolectric
key: robolectric-${{ runner.os }}-${{ hashFiles('**/gradle/libs.versions.toml', '**/src/test/resources/robolectric.properties') }}
restore-keys: robolectric-${{ runner.os }}-

- name: Resolve dependencies
run: ./gradlew --no-daemon :app:dependencies --configuration debugRuntimeClasspath

- name: Assemble
run: ./gradlew --no-daemon assembleDebug

- name: Unit tests
run: ./gradlew --no-daemon testDebugUnitTest

# Type-checks androidTest without an emulator.
- name: Compile instrumented tests
run: ./gradlew --no-daemon assembleAndroidTest

# Lint is not run: android:host="{your_domain}" is an intentional placeholder
# that lint reports as an AppLinkUrlError.

- name: Upload reports
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: build-reports
path: |
app/build/reports/
app/build/test-results/
retention-days: 7

# The ruleset requires a check named `test`; the job above reports as `build`.
# always() so a failed build still reports this check.
test:
name: test
needs: build
if: always()
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- run: '[ "${{ needs.build.result }}" = "success" ]'
8 changes: 2 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
.kotlin
.idea
89 changes: 77 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

# 📱 ShortIOApp – Android Sample Project for ShortIOSDK

[![CI](https://github.com/Short-io/android-sdk-example/actions/workflows/ci.yml/badge.svg)](https://github.com/Short-io/android-sdk-example/actions/workflows/ci.yml)

**ShortIOApp** is a sample Android project that demonstrates how to integrate and use the [ShortIOSDK](https://github.com/Short-io/android-sdk.git) for generating short links and handling deep links using [Short.io](https://short.io/).

This project helps developers understand how to:
Expand Down Expand Up @@ -28,13 +30,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 +60,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 +73,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 +89,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 +198,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
10 changes: 9 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ android {
buildFeatures {
compose = true
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}

dependencies {
Expand All @@ -49,8 +54,11 @@ 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)
testImplementation(libs.robolectric)
testImplementation(platform(libs.androidx.compose.bom))
testImplementation(libs.androidx.ui.test.junit4)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
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"
Loading
Loading