Skip to content

Integration Steps

Step 1: Add the SDK Dependency

Files: settings.gradle.kts (repository) and app/build.gradle.kts (dependency)

Add JitPack repository in settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}

Add the SDK dependency in app/build.gradle.kts:

dependencies {
    implementation("com.github.<org>:<glance-chat-sdk-android>:<version>")
}

Also add the required transitive dependencies:

dependencies {
    // Required transitive dependencies
    implementation(platform("com.google.firebase:firebase-bom:32.7.2"))
    implementation("com.google.firebase:firebase-firestore-ktx")
    implementation("com.google.firebase:firebase-auth-ktx")

    // Compose (skip if your app already includes Compose)
    val composeBom = platform("androidx.compose:compose-bom:2024.02.00")
    implementation(composeBom)
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.material3:material3")
    implementation("androidx.compose.material:material-icons-extended")
    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
    implementation("androidx.activity:activity-compose:1.8.2")

    // Image loading
    implementation("io.coil-kt:coil-compose:2.5.0")
    implementation("io.coil-kt:coil-svg:2.5.0")

    // Browser (for authentication flow)
    implementation("androidx.browser:browser:1.8.0")

    // Coroutines
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3")

    // Kotlin Serialization
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")

    // CameraX (required for selfie/try-on features)
    val cameraxVersion = "1.3.1"
    implementation("androidx.camera:camera-core:$cameraxVersion")
    implementation("androidx.camera:camera-camera2:$cameraxVersion")
    implementation("androidx.camera:camera-lifecycle:$cameraxVersion")
    implementation("androidx.camera:camera-view:$cameraxVersion")
}

Also ensure your build.gradle.kts has these settings:

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.8"
    }
}

And the serialization plugin in your project-level or app-level plugins:

plugins {
    id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22"
}

After adding dependencies, sync your project with Gradle to download and resolve all dependencies:

  • Android Studio: Click Sync Now in the banner that appears, or go to File → Sync Project with Gradle Files
  • Terminal: Run ./gradlew build or ./gradlew dependencies

Verify the sync completes without errors before proceeding.


Step 2: Initialize the SDK

File: app/src/main/java/{your.package.name}/YourApplication.kt

Typical path pattern

app/src/main/java/com/yourcompany/yourapp/YourApplication.kt

Initialize in your Application class's onCreate(). Glance will provide the complete config snippet with all values pre-filled.

import com.glance.chat.GlanceChatSDK
import com.glance.chat.config.GlanceChatConfig

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

        val config = GlanceChatConfig.Builder()
            .firebaseProjectId("your-firebase-project-id")
            .firebaseApiKey("your-firebase-api-key")
            .firebaseAuthDomain("your-auth-domain.firebaseapp.com")
            .firebaseStorageBucket("your-storage-bucket.appspot.com")
            .firebaseMessagingSenderId("your-sender-id")
            .firebaseAppId("your-app-id")
            .firebaseDatabaseId("(default)")
            .emberEndpoint("https://your-ember-endpoint.com")
            .environment(GlanceChatConfig.Environment.DEVELOPMENT)
            .productDeeplinkBaseUrl("yourapp://product")  // see step 5
            .build()

        GlanceChatSDK.init(
            context = this,
            partnerId = "your-partner-id",
            config = config
        )
    }
}

Make sure your Application class is registered in AndroidManifest.xml:

<application
    android:name=".YourApplication"
    ... >

Note

The SDK creates its own Firebase app instance named "glance_chat". It will not conflict with your app's existing Firebase setup.


Step 3: Add the Chat Overlay

File: app/src/main/java/{your.package.name}/presentation/MainActivity.kt

Typical path pattern

app/src/main/java/com/yourcompany/yourapp/MainActivity.kt or app/src/main/java/com/yourcompany/yourapp/presentation/MainActivity.kt

The GlanceChatOverlay composable renders the floating action button and the chat dialog. Place it as the last child in a Box on your main screen so it floats above all other content.

If Your App Uses Jetpack Compose

import com.glance.chat.ui.components.GlanceChatOverlay

@Composable
fun YourMainScreen() {
    Box(modifier = Modifier.fillMaxSize()) {
        // Your app content
        YourAppContent()

        // Add the chat overlay (FAB + chat dialog)
        GlanceChatOverlay()
    }
}

If Your App Uses XML Views (Legacy)

In your main Activity or Fragment, add a ComposeView programmatically or in XML.

Option A — In XML layout:

<!-- your_activity_main.xml -->
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Your existing views -->
    <include layout="@layout/your_existing_content" />

    <!-- Glance Chat overlay -->
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/glance_chat_overlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

Then in your Activity:

import com.glance.chat.ui.components.GlanceChatOverlay

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.your_activity_main)

        findViewById<ComposeView>(R.id.glance_chat_overlay).setContent {
            GlanceChatOverlay()
        }
    }
}

Option B — Programmatically:

import androidx.compose.ui.platform.ComposeView
import com.glance.chat.ui.components.GlanceChatOverlay

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.your_activity_main)

        // Add the Glance overlay on top of your existing layout
        val rootView = findViewById<ViewGroup>(android.R.id.content)
        val composeView = ComposeView(this).apply {
            setContent {
                GlanceChatOverlay()
            }
        }
        rootView.addView(composeView)
    }
}

Step 4: Set Page Context

The SDK needs to know which screen the user is on so the AI assistant can provide relevant responses. Call setPageContext() whenever the user navigates to a key screen.

Add this import wherever you call setPageContext(): import com.glance.chat.GlanceChatSDK

Page Types:

Page Type Description
PLP Product Listing Page
PDP Product Detail Page
CART Shopping Cart
HOME Home Page

Code Examples:

// For Product Listing Pages
GlanceChatSDK.setPageContext(
    context = "PLP",
    metadata = mapOf(
        "page_identifier" to collectionId,
        "current_url" to "https://your-store.com/collections/men-shirts",
        "tenant_id" to "your-tenant-id"
    )
)
// For Product Detail Pages
GlanceChatSDK.setPageContext(
    context = "PDP",
    metadata = mapOf(
        "product_id" to productId,
        "current_url" to "https://your-store.com/products/shirt-123",
        "tenant_id" to "your-tenant-id"
    )
)

Update the SDK with current page context when users navigate.


Files: - app/src/main/AndroidManifest.xml — for intent filters - app/src/main/java/{package}/presentation/MainActivity.kt — for handling deep links

When a user taps a product inside the SDK (chat recommendations, AI Store, try-on results), the SDK fires a deep link intent to navigate to that product in your app. You need to:

  1. Declare an intent filter in your manifest
  2. Handle the incoming deep link in your Activity

Add Intent Filter to AndroidManifest.xml:

<activity
    android:name=".MainActivity"
    android:launchMode="singleTask">

    <!-- Existing launcher intent -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <!-- Product deep link -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourapp" android:host="product" />
    </intent-filter>
</activity>

Important

  • The product deep link scheme (yourapp://product) must match productDeeplinkBaseUrl in Step 2.
  • Your Activity must use android:launchMode="singleTask" so deep links are delivered via onNewIntent().

Handle Deep Links in Your Activity:

class MainActivity : ComponentActivity() {

    private var pendingDeepLink by mutableStateOf<Uri?>(null)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleDeepLink(intent)
        setContent { MainApp(deepLink = pendingDeepLink) }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleDeepLink(intent)
    }

    private fun handleDeepLink(intent: Intent?) {
        intent?.data?.let { uri ->
            when {
                uri.scheme == "yourapp" && uri.host == "product" ->
                    pendingDeepLink = uri
                else ->
                    GlanceChatSDK.handleDeepLink(uri)  // OAuth callback
            }
        }
    }
}

Navigate to the PDP Screen:

The SDK passes the product's merchantVariantId in the deep link path. Resolve it to your internal product ID before navigating:

@Composable
fun MainApp(deepLink: Uri?) {
    val navController = rememberNavController()

    LaunchedEffect(deepLink) {
        deepLink?.let { uri ->
            if (uri.scheme == "yourapp" && uri.host == "product") {
                val rawId = uri.pathSegments.firstOrNull() ?: return@let
                // Resolve variant → product ID; fall back to rawId if not found
                val productId = variantMappingRepository.getProductIdForVariant(rawId) ?: rawId
                GlanceChatSDK.closeChat()
                navController.navigate("product/$productId")
            }
        }
    }

    // ... rest of your NavHost setup
}

Deep Link Format: The SDK constructs deep links as {productDeeplinkBaseUrl}/{merchantVariantId}. For example: yourapp://product/12345

Note

GlanceChatSDK.closeChat() dismisses the chat overlay before navigation so the user lands cleanly on the PDP screen.

Fallback Callback (Optional):

If productDeeplinkBaseUrl is not set, the SDK falls back to the onProductClick callback. This is useful during migration or for custom navigation logic:

GlanceChatSDK.onProductClick = { product ->
    // product.merchantVariantId contains the variant ID
    // product.id contains the SDK-internal product ID
    navigateToProduct(product.merchantVariantId ?: product.id)
}

Step 6 (Optional): Add Try-On Widget to Product Cards

File: Your product card composable or RecyclerView ViewHolder

The ProductTryOnWidget adds a small "try-on" button on individual product cards. The widget manages its own visibility — you do not need to control it manually.

Visibility Behavior:

  • Not shown while the product feed is loading (no placeholder space is reserved)
  • Not shown if no reference images are available for the variant
  • Not shown if the user is not logged in
  • Animates in from the bottom-right corner with a bouncy scale + fade once reference images are confirmed

The feed prefetch is triggered automatically when the composable enters composition, so by the time a user scrolls to a card the data is usually already cached.

Parameters:

Parameter Required Description
variantId Yes Merchant variant ID. Used to prefetch the product feed and check reference image availability.
onClick Yes Called when the user taps the widget. Set page context here to trigger the try-on flow.
modifier No Applied to the animated container. Typically Alignment.BottomEnd + padding.
enabled No Whether the widget is tappable (default true).
import com.glance.chat.ui.components.ProductTryOnWidget
import com.glance.chat.ui.theme.ChatDimens

@Composable
fun YourProductCard(
    product: Product,
    onTryOnClick: () -> Unit
) {
    // Resolve the merchant variant ID at composable scope so the widget
    // can start its background prefetch immediately on composition.
    val variantId = product.variants.firstOrNull { it.available }?.id
        ?: product.variants.firstOrNull()?.id
        ?: product.id

    Box {
        AsyncImage(
            model = product.imageUrl,
            contentDescription = product.name,
            modifier = Modifier.fillMaxWidth()
        )

        ProductTryOnWidget(
            variantId = variantId,
            onClick = {
                GlanceChatSDK.setPageContext(
                    context = "PDP",
                    metadata = mapOf(
                        "product_id" to variantId,
                        "page_identifier" to product.id,
                        "product_name" to product.name,
                        "product_image" to product.imageUrl,
                        "action" to "try_on",
                        "from_widget" to "true"
                    )
                )
                onTryOnClick()
            },
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(ChatDimens.ProductTryOnWidgetMargin)
        )
    }
}

Try-On Flow After Widget Tap:

  1. Prefetch (on composition)ProductFeedApi is called in the background to fetch reference images. The widget only becomes visible once this resolves with valid data.
  2. Try-On InitiationPOST /avatar/v1/generation/try with generationTrack: "TRY_ON" and the referenceImageId from the feed.
  3. Parallel data loads — Similar products and suggestion pills are fetched concurrently while generation runs.
  4. Status polling — Generation status is polled every second (max 60s) until complete.
  5. Result screen — Full-screen result shows the try-on image, similar products carousel, and contextual suggestion pills.

Step 7 (Optional): Session Expiry Callback

File: app/src/main/java/{your.package.name}/YourApplication.kt (same file as Step 2)

Add this inside your onCreate() method, after GlanceChatSDK.init():

override fun onCreate() {
    super.onCreate()

    // ... your config and GlanceChatSDK.init() from Step 2 ...

    // Add this after init():
    GlanceChatSDK.setSessionExpiredCallback {
        Log.d("YourApp", "Glance SDK session expired")  // <- replace "YourApp" with your tag
    }
}

Note

Requires import android.util.Log


Step 8 (Optional): Clean Up

File: app/src/main/java/{your.package.name}/YourApplication.kt (same file as Step 2)

Add onTerminate() as a separate method in your Application class (not inside onCreate()):

class YourApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        // ... SDK init ...
    }

    // Add this as a separate method at class level:
    override fun onTerminate() {
        super.onTerminate()
        GlanceChatSDK.destroy()
    }
}