A guide to integrating full-body scanning into your Android app.
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:
PrismSession drives the capture through a linear state machine. Your app
observes state changes via StateFlow and the SDK handles transitions automatically:
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.
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"
)
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()
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
)
RecordingMode.FRAMES — captures individual JPEG framesRecordingMode.VIDEO — captures continuous videoReviewMode.USER_REVIEW_ONLY — shows review screen before finishingReviewMode.FINAL_SCREEN_ONLY — skips review, goes straight to completionCreate 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()
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 */ }
}
}
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
}
}
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)
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
PrismSession — Main session controllerPrismSessionConfiguration — Session setup optionsPrismSessionState — State enum (IDLE through FINISHED)CaptureSession — Camera and ML Kit integrationApiClient — HTTP client for backend communicationUserClient — User creation and managementScanClient — Scan CRUD, upload URLs, measurements, assetsPrismRecording — Frame storage and archive packagingPrismThemeConfiguration — UI color, font, and styling customizationPoseTheme — Pose overlay visualization stylingSee the API Reference for full class and method documentation.