> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getdesmo.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Android SDK Quickstart

> Install the Desmo Android SDK via JitPack and record your first delivery session.

## What you'll build

In this quickstart you will:

* Install the **Desmo Android SDK** via **JitPack**.
* Initialize the SDK with your **publishable key**.
* Start and stop a **delivery session** from your Android app.

This page assumes you already have a Desmo account and at least one **publishable key** that starts with `pk_...`.\
If you don't have a key yet, contact your Desmo representative to have one issued for your project.

## Requirements

* Android 7.0 (API 24) or later
* Android Gradle Plugin compatible with **Kotlin 1.9+**
* Your app is written in **Kotlin** (the SDK is a Kotlin library)

### App permissions

To let Desmo record sessions correctly, your app should request **location** permissions so sessions can be tied to where the delivery happened.

Add these to your `AndroidManifest.xml`:

```xml theme={null}
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
```

On **Android 13+**, you also need notification permission for the background tracking notification:

```xml theme={null}
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
```

On Android 6.0+ you must also request these at runtime. Later on this page we'll show how to ask for the permissions using the helpers exposed by the SDK.

## 1. Install the SDK (JitPack)

The Android SDK is distributed via **JitPack** from the GitHub repo `kubocreate/desmo-android-sdk`.

### 1.1 Add the JitPack repository

In your **app project** (not the SDK repo), open the **root** `settings.gradle.kts` and make sure the `dependencyResolutionManagement` block includes JitPack:

```kotlin theme={null}
// settings.gradle.kts (Kotlin DSL)
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven(url = "https://jitpack.io")
    }
}
```

If you use Groovy and have `settings.gradle`:

```groovy theme={null}
// settings.gradle (Groovy)
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
```

If your project still uses a top-level `build.gradle` with an `allprojects { repositories { ... } }` block instead of `dependencyResolutionManagement`, add the `jitpack.io` `maven` entry there:

```groovy theme={null}
// Top-level build.gradle (older projects)
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
```

### 1.2 Add the dependency

In your **app module** `build.gradle` (usually `app/build.gradle`):

1. **Remove** any old module dependency, for example:

   ```groovy theme={null}
   implementation project(':desmo-android-sdk')
   ```

2. **Add** the JitPack dependency:

   ```groovy theme={null}
   dependencies {
       implementation 'com.github.kubocreate:desmo-android-sdk:v1.0.2'
   }
   ```

If you use Kotlin DSL for your app module, the equivalent is:

```kotlin theme={null}
dependencies {
    implementation("com.github.kubocreate:desmo-android-sdk:v1.0.2")
}
```

Here `v1.0.2` is the Git tag for the SDK release on GitHub. When a new version is tagged, update this value to match.

If you previously copied any Desmo code directly into your project, or used old Maven Central coordinates or local module references, **remove those** and keep **only** the JitPack dependency to avoid duplicate classes.

## 2. Initialize the SDK

Call `Desmo.setup` once at app startup, usually in your custom `Application` class.

```kotlin theme={null}
import android.app.Application
import io.getdesmo.tracesdk.Desmo
import io.getdesmo.tracesdk.config.DesmoEnvironment

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()

        // Initialize the SDK
        Desmo.setup(
            context = this,
            apiKey = "pk_sandbox_XXXXXXXXXXXXXXXX", // your Desmo publishable key
            environment = DesmoEnvironment.SANDBOX  // use LIVE in production
        )

        // Recommended: Bind to lifecycle for automatic sensor management
        Desmo.bindToProcessLifecycle()
    }
}
```

Then register your `Application` class in `AndroidManifest.xml`:

```xml theme={null}
<application
    android:name=".MyApp"
    ...>
    <!-- activities etc -->
</application>
```

* `apiKey`: your **publishable** Desmo API key (starts with `pk_`).
* `environment`:
  * `DesmoEnvironment.SANDBOX` – Desmo sandbox environment.
  * `DesmoEnvironment.LIVE` – Desmo production environment.

If `Desmo.setup` fails (for example, if the key does not start with `pk_`), the SDK will log an error and `Desmo.client` will remain `null`.

### Lifecycle binding (recommended)

`Desmo.bindToProcessLifecycle()` automatically manages sensor registration when your app moves between foreground and background. This ensures sensors are re-registered properly after Android throttles them in the background.

### Without a `Context` (no telemetry)

There is also a `setup(apiKey, environment)` overload that does **not** take a `Context`. This is useful in non-Android environments, but it disables on-device telemetry. For typical courier apps you should prefer the `setup(context, apiKey, environment)` version so telemetry is enabled.

## 3. Request permissions at runtime

The SDK exposes helpers so you know which permissions are required and whether they are granted:

* `Desmo.getRequiredPermissions() : Array<String>`
* `Desmo.hasRequiredPermissions(context) : Boolean`
* `Desmo.getMissingPermissions(context) : List<String>`

Example with an `Activity`:

```kotlin theme={null}
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import io.getdesmo.tracesdk.Desmo

class DeliveryActivity : AppCompatActivity() {

    private val requestPermissionsLauncher =
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { _ ->
            // You can re-check Desmo.hasRequiredPermissions(this) here if needed
        }

    private fun ensureDesmoPermissions() {
        if (!Desmo.hasRequiredPermissions(this)) {
            val missing = Desmo.getMissingPermissions(this).toTypedArray()
            requestPermissionsLauncher.launch(missing)
        }
    }
}
```

Call `ensureDesmoPermissions()` before you start a session so location is available.

### Notification permission (Android 13+)

On Android 13 and later, you need to request notification permission separately for the background tracking notification:

```kotlin theme={null}
private fun ensureNotificationPermission() {
    if (!Desmo.canShowNotifications(this)) {
        val permissions = Desmo.getForegroundServicePermissions()
        if (permissions.isNotEmpty()) {
            requestPermissionsLauncher.launch(permissions)
        }
    }
}
```

<Note>
  If notification permission is denied, sessions will still work when the app is in the foreground. However, background tracking won't function without the notification.
</Note>

## 4. Understanding session types

Every session requires a **session type** that indicates what the driver is doing:

| SessionType           | When to use                                              |
| --------------------- | -------------------------------------------------------- |
| `SessionType.PICKUP`  | Driver is collecting a package from a merchant/warehouse |
| `SessionType.DROP`    | Driver is delivering a package to a customer             |
| `SessionType.TRANSIT` | Driver is traveling between stops (no specific address)  |

<Note>
  For `PICKUP` and `DROP` sessions, you should provide an `address`. For `TRANSIT` sessions, the address is optional.
</Note>

## 5. Understanding the Address model

The SDK provides a structured `Address` model for delivery locations:

```kotlin theme={null}
import io.getdesmo.tracesdk.models.Address

// Option 1: Structured address (preferred for better insights)
val structuredAddress = Address(
    line1 = "123 Main Street",
    line2 = "Apt 4B",
    city = "San Francisco",
    state = "CA",
    postalCode = "94102",
    country = "US",
    lat = 37.7749,
    lng = -122.4194
)

// Option 2: Raw address string (when you only have unstructured data)
val rawAddress = Address.fromRaw("123 Main Street, San Francisco, CA 94102")
```

Use structured addresses when possible — they provide better location context for delivery insights.

## 6. Start a delivery session

Call this when you begin recording a delivery. The SDK uses a **crash-safe** `DesmoResult` pattern that guarantees errors will never crash your app.

```kotlin theme={null}
import androidx.lifecycle.lifecycleScope
import io.getdesmo.tracesdk.Desmo
import io.getdesmo.tracesdk.models.Address
import io.getdesmo.tracesdk.models.SessionType
import kotlinx.coroutines.launch

fun startDeliverySession() {
    val client = Desmo.client
    if (client == null) {
        println("Desmo SDK is not configured")
        return
    }

    lifecycleScope.launch {
        client.startSession(
                deliveryId = "DELIVERY_123",
            sessionType = SessionType.DROP,
            address = Address.fromRaw("123 Main Street, San Francisco")
        ).onSuccess { session ->
            println("Started session: ${session.sessionId}")
        }.onFailure { error ->
            // SDK errors never crash your app - handle gracefully
            println("Failed to start session: $error")
        }
    }
}
```

### Required parameters

| Parameter     | Type          | Description                             |
| ------------- | ------------- | --------------------------------------- |
| `deliveryId`  | `String`      | Your internal delivery/order identifier |
| `sessionType` | `SessionType` | `PICKUP`, `DROP`, or `TRANSIT`          |

### Optional parameters

| Parameter         | Type             | Description                                                        |
| ----------------- | ---------------- | ------------------------------------------------------------------ |
| `address`         | `Address?`       | Delivery address (required for PICKUP/DROP, optional for TRANSIT)  |
| `externalRiderId` | `String?`        | Your system's driver/rider ID                                      |
| `startLocation`   | `StartLocation?` | Override the starting GPS position (auto-captured if not provided) |

### Full example with all parameters

```kotlin theme={null}
import io.getdesmo.tracesdk.models.Address
import io.getdesmo.tracesdk.models.SessionType
import io.getdesmo.tracesdk.models.StartLocation

client.startSession(
    deliveryId = "ORDER_12345",
    sessionType = SessionType.DROP,
    externalRiderId = "DRIVER_789",
    address = Address(
        line1 = "456 Oak Avenue",
        city = "San Francisco",
        state = "CA",
        postalCode = "94102",
        country = "US"
    ),
    startLocation = StartLocation(lat = 37.7749, lng = -122.4194)
).onSuccess { session ->
    // Session started successfully
}.onFailure { error ->
    // Handle error (delivery flow should continue)
}
```

Behind the scenes, the SDK:

* Calls Desmo's backend to create a session.
* Starts collecting telemetry on-device (when initialized with a `Context`).
* Starts a foreground service for background tracking.
* Begins batching telemetry to Desmo while the session is recording.

## 7. Stop a delivery session

Call this when the delivery is complete:

```kotlin theme={null}
fun stopDeliverySession() {
    val client = Desmo.client
    if (client == null) {
        println("Desmo SDK is not configured")
        return
    }

    lifecycleScope.launch {
        client.stopSession()
            .onSuccess { session ->
            println("Stopped session: ${session.sessionId}")
            }
            .onFailure { error ->
                // Consider retrying - telemetry is saved locally
                println("Failed to stop session: $error")
        }
    }
}
```

When you stop the session, the SDK:

* Flushes any remaining telemetry.
* Notifies the backend that the session is complete.
* Stops the background tracking service and removes the notification.
* Lets Desmo's workers start processing PoD intelligence for that session.

## 8. The DesmoResult pattern

The SDK uses a crash-safe `DesmoResult` pattern instead of throwing exceptions. This guarantees SDK errors will **never crash your app**.

```kotlin theme={null}
// Pattern 1: Fluent callbacks (recommended)
client.startSession(...)
    .onSuccess { session -> /* use session */ }
    .onFailure { error -> /* log error, continue delivery */ }

// Pattern 2: Explicit when
when (val result = client.startSession(...)) {
    is DesmoResult.Success -> useSession(result.data)
    is DesmoResult.Failure -> logError(result.error)
}

// Pattern 3: Fire and forget
val session = client.startSession(...).getOrNull() // null if failed
```

<Note>
  Always handle `onFailure` gracefully. The driver's delivery flow should continue even if the SDK encounters an error.
</Note>

## 9. Background operation

The SDK automatically keeps tracking when the app goes to background. This is critical for delivery apps where drivers put their phone in their pocket.

### How it works

When you call `startSession()`, the SDK automatically:

1. Starts a **foreground service** to keep the app alive
2. Shows a **notification** (required by Android) indicating tracking is active
3. Continues collecting location and sensor data even when the screen is off

When you call `stopSession()`, the service stops and the notification disappears.

### Default notification

By default, the notification uses a generic location icon with text:

* **Title:** "Recording Active"
* **Text:** "Tracking delivery in progress"

### Customizing the notification (optional)

To show your own branding, call `configureForegroundService()` after setup:

```kotlin theme={null}
Desmo.setup(this, apiKey, environment)
Desmo.bindToProcessLifecycle()

// Optional: Customize the notification with your branding
Desmo.configureForegroundService(
    title = "SwiftDrop Delivery",
    text = "Recording your route...",
    smallIconResId = R.drawable.ic_delivery
)
```

For full control, you can provide your own `Notification` object:

```kotlin theme={null}
val notification = NotificationCompat.Builder(this, "my_channel")
    .setContentTitle("SwiftDrop Active")
    .setContentText("3 stops remaining")
    .setSmallIcon(R.drawable.ic_swiftdrop)
    .setColor(0xFF00B140.toInt())
    .build()

Desmo.configureForegroundService(notification)
```

<Note>
  If you provide a custom notification, you must create the notification channel yourself before starting a session.
</Note>

## Next steps

From here:

* **Configure keys and environments** in [Android Auth & Environments](/essentials/android-auth-environments).
* **Enable logging and troubleshoot** in [Android Logging, Debugging & Troubleshooting](/essentials/android-logging-debugging).
* Revisit the **Desmo Integration Overview** at [Integrating Desmo](/) to see how the Android and iOS SDKs fit with your backend APIs and dashboard.
