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

# Cello for Android

The Cello SDK for Android enables you to add a referral program into your Android app. With a plug-n-play mobile component, your users can easily share their invite link with their friends and network using mobile sharing options convenient for them, receive rewards and get paid out.

## Installation

You can install Cello for Android using Gradle or manually. A basic installation takes around 15 minutes but will take a little longer if you want to customize the way the Cello Referral Component is launched.

### Compatibility

Cello SDK for Android is compatible with API 21 and up.

### SDK size

The size of Cello for Android once installed varies depending on your app’s configuration. Around 7MB is the average size increase we would expect to see if you're minifying your app correctly.

## Setup

Install Cello to see and give your users the option to spread the word from your Android app. Cello for Android supports API 21 and above.

**Note:** We recommend using the latest available `compileSdkVersion`.

### Install Cello

Add the following dependency to your app’s `build.gradle` file:

#### Groovy (or Kotlin DSL)

```gradle theme={null}
dependencies {
    implementation("so.cello.android:cello-sdk:0.9.1")
}
```

Also, ensure that Maven Central is added to your root `build.gradle`:

```gradle theme={null}
allprojects {
    repositories {
        mavenCentral()
    }
}
```

### Choose an Environment

In your Cello SDK setup, you have the flexibility to select the environment in which your application will run. This feature is especially useful for different stages of development, such as testing in a development or staging environment before going live in production. The available environments are:

* `prod` (Production) – *default*
* `sandbox` (Sandbox)

#### Configuration Steps

In your Android project, open or create `res/values/config.xml`, then add:

```xml theme={null}
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="cello_env">prod</string>
</resources>
```

To change the environment, simply replace the value of `cello_env`. For instance, to set the environment to sandbox:

```xml theme={null}
<string name="cello_env">sandbox</string>
```

Save and rebuild your project to apply.

Using this configuration, the Cello SDK will adapt to the specified environment, allowing for more controlled development and testing processes.

## Initialize Cello

In this step, you will need your **product ID** and a **token** you have generated for the user, similar when [**implementing the web based Referral component**](https://docs.cello.so/docs/user-authentication).

`initialize` takes an `Activity`, so call it from your activity rather than from `Application`:

```kotlin theme={null}
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Cello.initialize(this, "YOUR_PRODUCT_ID", token)
    }
}
```

Calling `initialize` again with a different activity updates the activity the SDK holds, so it is safe to call from each activity that needs the referral component.

### Knowing when the SDK is ready

`initialize` returns immediately and does its work in the background. Pass `onComplete` to find out when it has finished, and whether it failed. Available from 0.15.0.

```kotlin theme={null}
Cello.initialize(this, "YOUR_PRODUCT_ID", token) { result ->
    when (result) {
        is CelloInitializationResult.Success ->
            Log.d("Cello", "ready: ${result.configuration.optString("referrerId")}")

        is CelloInitializationResult.Failure ->
            Log.e("Cello", "initialization failed", result.error)
    }
}
```

The callback fires once per call, on the main thread, whether initialization succeeds or fails.

You do not need to wait for it before calling `openWidget`, `showFab`, `updateToken`, `changeLanguage` or `setThemeMode`. Those are queued while initialization is in progress and run once it completes.

<Note>
  Before 0.15.0 there was no way to observe the result. A failed initialization
  was only logged, so a later `openWidget` call would silently do nothing.
</Note>

## Customize the Cello Referral Component

The Cello SDK allows for various levels of customization to better fit into your app's design and flow. One of the main components you might want to customize is the [**Referral component**](https://docs.cello.so/docs/component-overview)

You have two options to launch the referral component:

### Default Launcher

If you choose to go with the default launcher, you can call the `showFab()` method from the Cello SDK to present a Floating Action Button (FAB) within your app. This FAB is pre-styled but may not perfectly match your app's look and feel.

```kotlin theme={null}
Cello.client().showFab()
```

### Custom Launcher

If the default launcher does not fit your needs, you can implement your own custom launcher. This could be any UI element like a button, menu item, or even a gesture. To open the Referral component using a custom launcher, you can call `Cello.openWidget()`.

```kotlin theme={null}
Cello.client().openWidget()
```

Example using Compose:

```kotlin theme={null}
Button(onClick = { Cello.client().openWidget() }) {
    Text("Open Referral")
}
```

## Android API

### `Cello.initialize()`

Initializes the Cello referral component.

| Name | Type | Description | Required |
| - | - | - | - |
| activity | Activity | This is the reference to the MainActivity | Yes |
| productId | String | Identifier of the product your users will refer | Yes |
| token | String | Access token generated for the given user | Yes |
| productUserDetails | ProductUserDetails | Product user details | No |
| language | String | Initial language of the widget | No |
| themeMode | String | Initial theme mode: `"light"` or `"dark"` | No |

```kotlin theme={null}
import com.cello.cello_sdk.ProductUserDetails

val productUserDetails = ProductUserDetails(
    firstName = "John",
    lastName = "Doe",
    fullName = "John Doe",
    email = "john.doe@example.com"
)

Cello.initialize(this, "YOUR_PRODUCT_ID", token, productUserDetails = productUserDetails, language = 'de', themeMode = 'light')
```

### `Cello.showFab()`

Shows the Floating action button or bookmark that launches the Referral Component

```kotlin theme={null}
Cello.client().showFab()
```

### `Cello.hideFab()`

Hides the Floating action button or bookmark that launches the Referral Component

```kotlin theme={null}
Cello.client().hideFab()
```

### `Cello.openWidget()`

Opens the referral component.

```kotlin theme={null}
Cello.client().openWidget()
```

### `Cello.hideWidget()`

Hides the referral component.

```kotlin theme={null}
Cello.client().hideWidget()
```

### `Cello.getActiveUcc()`

A method to get an active `ucc` and invite link for the currently logged in user.

```kotlin theme={null}
val result = Cello.client()?.getActiveUcc()
// mapOf("ucc" to "...", "link" to "...") or null before initialization
```

### `Cello.getConfiguration()`

Returns the state of the initialised SDK, or `null` if the SDK is not ready yet. `null` means "not initialised", not "this user has no referral data".

Available from 0.15.0.

```kotlin theme={null}
val configuration = Cello.client()?.getConfiguration()

if (configuration != null) {
    Log.d("Cello", "${configuration.ucc} ${configuration.shareLink}")
    Log.d("Cello", "${configuration.views} ${configuration.unreadViewsCount}")
}
```

Exposes `productId`, `referrerId`, `campaignId`, `ucc`, `shareLink`, `productName`, `language`, `supportedLanguages`, `themeMode`, `platform`, `sdkVersion`, `tokenTTL`, `totalEarnedRewards`, `views`, `unreadViewsCount`, `hasPaymentDetails`, `onboardingShown`, `isUserBlocked`, `isWidgetUnavailable`, `showFullWidget`, `showEnhancedView`, `isTablet` and `campaignConfig`.

`tokenTTL` is `-1` when token expiry checking is disabled for the product, which is the default.

Use `toMap()` if you need a serialisable form, for example to pass across a bridge.

This replaces the older `configuration` property, which is deprecated. That property returns the raw initialization response and an empty object before the SDK is ready, so there is no way to tell "not ready" from "no data".

## `getCampaignConfig()`

Returns campaign config values for the currently logged-in user.

```kotlin theme={null}
let result = Cello.client().getCampaignConfig()
```

### Returns

<ResponseField name="campaignConfig" type="object">
  The campaign config object:

  <Expandable title="properties">
    <ResponseField name="primaryCurrency" type="string">
      Primary currency code
    </ResponseField>

    <ResponseField name="revenuePercentage" type="number">
      Percentage of attributed new revenue that will be paid as a reward
    </ResponseField>

    <ResponseField name="rewardCap" type="number">
      Maximum reward that can be earned per referral
    </ResponseField>

    <ResponseField name="newSignupBonus" type="number">
      Additional reward for signups to encourage more sharing
    </ResponseField>

    <ResponseField name="newPurchaseBonus" type="number">
      Additional reward for purchases to encourage more sharing
    </ResponseField>

    <ResponseField name="newUserDiscountMonth" type="number">
      How long new users get a discount
    </ResponseField>

    <ResponseField name="newUserDiscountPercentage" type="number">
      The discount new users get
    </ResponseField>
  </Expandable>
</ResponseField>

### `Cello.changeLanguage()`

A method to change the language of the Referral component at runtime without re-initialising it.

```kotlin theme={null}
Cello.client().changeLanguage("de")
```

### `Cello.setThemeMode()`

A method to change the theme mode of the Referral component at runtime without re-initialising it.

```kotlin theme={null}
Cello.client().setThemeMode("dark")
```

**Parameters:**

* `themeMode` (*String*): The theme mode to set. Valid values are `"light"` or `"dark"`.

### `Cello.shutdown()`

Shuts down connection to Cello and unmounts the component

```kotlin theme={null}
Cello.client().shutdown()
```

***

## Error Handling

### Common Error Scenarios

#### 1. Invalid Parameters

**What it means:** The product ID or token provided during initialization is empty or blank.

**Error behavior:**

* SDK initialization is **silently skipped**
* Warning logged: `"Initialization skipped: productId and token must not be empty or blank"`
* No exception thrown

**Common causes:**

* Empty strings for productId or token
* Whitespace-only strings

***

#### 2. Network/API Errors

**What it means:** The network request to initialize the SDK or update token failed.

**Error type:** `RuntimeException("Response not successful")`

**Common causes:**

* No internet connection
* Server errors (5xx responses)
* HTTP errors (4xx responses)
* Timeout issues
* Firewall or proxy blocking requests

***

#### 3. Activity State Errors

**What it means:** The Activity is destroyed, finishing, or not available when trying to perform UI operations.

**Error type:** `IllegalStateException("Activity is not valid")`

**Error message:** `"Cannot perform UI operation - Activity is not available or destroyed"`

**Common causes:**

* Calling SDK methods after Activity is destroyed
* Activity finishing during async operations
* Attempting UI operations during Activity transitions

***

#### 4. Initialization Failures

**What it means:** The SDK failed to initialize due to an error in the initialization process.

**Error behavior:**

* Exception passed to callback
* Error logged: `"Error initializing widget: {exception message}"`
* Pending operations cleared

**Common causes:**

* Network connectivity issues
* Invalid credentials (product ID or token)
* Server-side configuration issues

***

### Error Handling Best Practices

**1. Always handle both success and error cases:**

```kotlin theme={null}
Cello.initialize(this, productId, token) { config, exception ->
    if (exception != null) {
        // Handle error
        Log.e("Cello", "Error: ${exception.message}")
        showErrorMessage("Unable to initialize referral system")
    } else {
        // Handle success
        Log.d("Cello", "Initialized successfully")
        setupReferralFeatures(config)
    }
}
```

**2. Validate parameters before SDK calls:**

```kotlin theme={null}
fun initializeCello(productId: String, token: String) {
    // Validate product ID
    if (productId.isBlank()) {
        Log.e("Cello", "Product ID cannot be empty")
        showError("Configuration error")
        return
    }

    // Validate token
    if (token.isBlank()) {
        Log.e("Cello", "Token cannot be empty")
        showError("Authentication error")
        return
    }

    // Check Activity state
    if (isFinishing || isDestroyed) {
        Log.e("Cello", "Cannot initialize - Activity is finishing")
        return
    }

    Cello.initialize(this, productId, token) { config, error ->
        // Handle result
    }
}
```

**3. Implement retry logic for network errors:**

```kotlin theme={null}
class CelloManager(private val activity: Activity) {
    private var retryCount = 0
    private val maxRetries = 3

    fun initializeWithRetry(productId: String, token: String) {
        Cello.initialize(activity, productId, token) { config, exception ->
            if (exception != null) {
                handleInitializationError(exception, productId, token)
            } else {
                retryCount = 0
                onInitializationSuccess(config)
            }
        }
    }

    private fun handleInitializationError(
        exception: Exception,
        productId: String,
        token: String
    ) {
        Log.e("Cello", "Initialization error: ${exception.message}")

        // Retry for network errors
        if (exception is RuntimeException && retryCount < maxRetries) {
            retryCount++
            val delay = (2000L * retryCount) // Linear backoff

            Handler(Looper.getMainLooper()).postDelayed({
                Log.d("Cello", "Retrying initialization (attempt $retryCount)")
                initializeWithRetry(productId, token)
            }, delay)
        } else {
            // Max retries reached or non-recoverable error
            retryCount = 0
            showFinalError("Failed to initialize after $maxRetries attempts")
        }
    }
}
```

**4. Track initialization state:**

```kotlin theme={null}
class CelloStateManager(private val activity: Activity) {
    private var initializationState = InitState.NOT_INITIALIZED

    enum class InitState {
        NOT_INITIALIZED,
        INITIALIZING,
        INITIALIZED,
        FAILED
    }

    fun initialize(productId: String, token: String) {
        if (initializationState == InitState.INITIALIZING) {
            Log.w("Cello", "Initialization already in progress")
            return
        }

        initializationState = InitState.INITIALIZING

        Cello.initialize(activity, productId, token) { config, exception ->
            if (exception != null) {
                initializationState = InitState.FAILED
                Log.e("Cello", "Initialization failed")
            } else {
                initializationState = InitState.INITIALIZED
                Log.d("Cello", "Initialization successful")
            }
        }
    }

    fun showWidget() {
        when (initializationState) {
            InitState.NOT_INITIALIZED -> {
                Log.w("Cello", "SDK not initialized")
            }
            InitState.INITIALIZING -> {
                Log.w("Cello", "SDK still initializing")
            }
            InitState.FAILED -> {
                Log.e("Cello", "Cannot show widget - initialization failed")
            }
            InitState.INITIALIZED -> {
                Cello.openWidget()
            }
        }
    }
}
```

**5. Handle Activity lifecycle properly:**

```kotlin theme={null}
class MainActivity : AppCompatActivity() {
    private var isCelloReady = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initializeCello()
    }

    private fun initializeCello() {
        Cello.initialize(this, productId, token) { config, error ->
            if (error == null && !isFinishing && !isDestroyed) {
                isCelloReady = true
            }
        }
    }

    fun performCelloAction() {
        // Always check Activity and SDK state
        if (isFinishing || isDestroyed) {
            Log.w("Cello", "Activity is finishing - cannot perform action")
            return
        }

        if (!isCelloReady) {
            Log.w("Cello", "SDK not ready")
            return
        }

        Cello.openWidget()
    }

    override fun onDestroy() {
        super.onDestroy()
        if (isFinishing) {
            isCelloReady = false
            Cello.shutdown()
        }
    }
}
```

### Getting Help

If you encounter an error you can't resolve:

1. Check the error scenario in the reference above
2. Verify your integration follows the code examples
3. Check logcat for detailed error messages: `adb logcat | grep cello-sdk`
4. Verify network connectivity and firewall settings
5. Ensure product ID and token are valid and not empty
6. Check Activity lifecycle (not finishing or destroyed)
7. Contact Cello support with:
   * Full error message and stack trace from logcat
   * Steps to reproduce
   * SDK version
   * Android OS version and device model
   * Network conditions when error occurred


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.