← Back to versions

Getting Started with PrismSDK Android

A guide to integrating full-body scanning into your Android app.

Overview

PrismSDK captures full-body scans using the device camera and ML Kit pose detection. The SDK handles the entire capture workflow — camera setup, user positioning guidance, pose validation, recording, and packaging — through a state machine that your app observes.

The end-to-end flow involves four stages:

  1. Setup — Initialize the API client and create a user
  2. Capture — Configure and run a scan session
  3. Upload — Create a scan record and upload the archive
  4. Results — Retrieve measurements and 3D assets

Session State Machine

PrismSession drives the capture through a linear state machine. Your app observes state changes via StateFlow and the SDK handles transitions automatically:

IDLE → VOLUME → LEVELING → POSITIONING → POSING → RECORDING → PROCESSING → USER_REVIEW → FINISHED

Each state corresponds to a step the user completes — adjusting volume, leveling the device, positioning their body, holding the correct pose, and then the actual recording. The SDK provides built-in UI assistants for each step.

Integration Steps

1 Initialize the API Client

Create an ApiClient with your backend URL and credentials. This client is shared by UserClient and ScanClient.

val apiClient = ApiClient(
    baseURL = "https://api.example.com",
    clientCredentials = "your-credentials"
)

2 Create a User

A user must exist before scans can be created. Use UserClient to create or fetch users. Terms of Service must be accepted.

val userClient = UserClient(apiClient)

val newUser = NewUser(
    token = "your-user-token",
    email = "user@example.com",
    sex = UserSex.MALE,
    region = UserRegion.NORTH_AMERICA,
    usaResidence = null,
    birthDate = LocalDate.of(1990, 1, 1),
    weight = Weight(180.0, WeightUnit.POUNDS),
    height = Height(72.0, HeightUnit.INCHES),
    researchConsent = false,
    termsOfService = TermsOfService(accepted = true, version = "1.0")
)

val user = userClient.createUser(newUser).getOrThrow()

3 Configure the Session

Set up PrismSessionConfiguration to control recording mode, review behavior, and visual theming.

val config = PrismSessionConfiguration(
    reviewMode = ReviewMode.USER_REVIEW_ONLY,
    recordingMode = RecordingMode.FRAMES,
    theme = PrismThemeConfiguration(),   // customize colors, fonts
    poseTheme = PoseTheme()              // customize pose overlay
)

4 Create and Start the Session

Create a CaptureSession and PrismSession, then start the capture workflow.

// Check device support first
val captureSession = CaptureSession(context)
if (!captureSession.isSupported()) {
    // Device doesn't support ML Kit or lacks front camera
    return
}

val session = PrismSession(
    context = context,
    lifecycleOwner = lifecycleOwner,
    captureSession = captureSession,
    configuration = config
)

session.start()

5 Observe Session State

Collect state changes from session.currentState to update your UI and react to the capture workflow progressing.

// In a coroutine scope
session.currentState.value.collect { state ->
    when (state) {
        PrismSessionState.IDLE -> { /* Ready */ }
        PrismSessionState.VOLUME -> { /* Volume adjustment */ }
        PrismSessionState.LEVELING -> { /* Device leveling */ }
        PrismSessionState.POSITIONING -> { /* Body positioning */ }
        PrismSessionState.POSING -> { /* Pose validation */ }
        PrismSessionState.RECORDING -> { /* Capture in progress */ }
        PrismSessionState.PROCESSING -> { /* Packaging archive */ }
        PrismSessionState.USER_REVIEW -> { /* User reviewing scan */ }
        PrismSessionState.FINISHED -> { /* Capture complete */ }
        PrismSessionState.ERROR -> { /* Handle error */ }
    }
}

// Also observe errors
session.sessionError.collect { error ->
    when (error) {
        is ArchiveError.InsufficientFrames -> { /* Not enough frames */ }
        is ArchiveError.FileOperationFailed -> { /* I/O error */ }
        null -> { /* No error */ }
    }
}

6 Retrieve the Recording Archive

When the session reaches FINISHED, the recording archive (a .zip file) and an optional preview GIF are available.

session.recordingArchive.collect { archiveFile ->
    archiveFile?.let {
        // archiveFile is a .zip containing:
        // - JPEG frames (downsampled to ~150)
        // - Pose metadata
        // - Camera metadata
    }
}

session.gifFile.collect { gif ->
    gif?.let {
        // Optional preview GIF of the scan
    }
}
The SDK validates that at least 70 frames were captured and downsamples to 150 preferred frames using a Bresenham algorithm before packaging the archive.

7 Create a Scan and Upload

After capture, create a scan record in the backend, get a presigned upload URL, and upload the archive.

val scanClient = ScanClient(apiClient)

// Create the scan record
val newScan = NewScan(
    userToken = user.token,
    deviceConfigName = "device-config"
)
val scan = scanClient.createScan(newScan).getOrThrow()

// Get presigned upload URL
val uploadUrl = scanClient.uploadUrl(scan.id).getOrThrow()

// Upload the archive .zip to uploadUrl.url via HTTP PUT
// (use your preferred HTTP client for the upload)

8 Retrieve Results

Once the backend processes the scan, retrieve measurements and 3D assets.

// Poll or check scan status
val scan = scanClient.getScan(scanId).getOrThrow()
// scan.status: CREATED -> PROCESSING -> READY (or FAILED)

// Get body measurements (28 circumference values)
val measurements = scanClient.measurements(scanId).getOrThrow()
// measurements.waistCircumference, measurements.chestCircumference, etc.

// Get 3D model and asset URLs
val assets = scanClient.assetUrls(scanId).getOrThrow()
// assets.previewImage, assets.model, assets.stripes, assets.texture

Key Classes Reference

See the API Reference for full class and method documentation.

Requirements