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 buildor./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.
Step 5: Handle Deep Links¶
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:
- Declare an intent filter in your manifest
- 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 matchproductDeeplinkBaseUrlin Step 2. - Your Activity must use
android:launchMode="singleTask"so deep links are delivered viaonNewIntent().
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:
- Prefetch (on composition) —
ProductFeedApiis called in the background to fetch reference images. The widget only becomes visible once this resolves with valid data. - Try-On Initiation —
POST /avatar/v1/generation/trywithgenerationTrack: "TRY_ON"and thereferenceImageIdfrom the feed. - Parallel data loads — Similar products and suggestion pills are fetched concurrently while generation runs.
- Status polling — Generation status is polled every second (max 60s) until complete.
- 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()
}
}