Smart Camera Pixelnetica™ Document Scanning SDK for Android
The camera library provides a ready-made camera activity: it watches the live preview for a document, guides the user with an on-screen frame and hints, captures automatically when the shot is good, and hands the captured pages back to your code as a list of image URIs. You launch it with one call and receive results through the standard Activity Result APIs — no camera code of your own.
Include into the Project
Add the dependency to your module-level build script:
dependencies {
implementation("com.pixelnetica.sdk:camera:3.2.0")
// Other dependencies
}
Or, if you use the Version Catalog:
dependencies {
implementation(libs.pixelnetica.camera)
// Other dependencies
}
Usage
Register the CameraContract in your Activity, Fragment, or Composable:
In an Activity or Fragment
private val cameraRequest = registerForActivityResult(CameraContract()) { uriList ->
// Handle the result
}
In a Composable
val cameraLauncher = rememberLauncherForActivityResult(
contract = CameraContract()
) { uriList ->
// Handle the result
}
The camera screen is a regular activity behind an ActivityResultContract, so hosting it in a Compose application needs nothing special — the launcher above is the complete integration.
Launching the Camera Activity
cameraLauncher.launch(CameraContract.CameraParams())
Configuring the Launch
CameraParams controls how the camera screen behaves for this launch. Every field has a working default, so CameraParams() is a complete configuration:
outputDir— the directory where captured pages are written. Leftnull, the activity uses its own storage.tmpPrefix— the file-name prefix for the image files it creates.authority— the authority of aFileProvideryour application declares. With it set, results come back ascontent://URIs your app can grant to other apps; leftnull, they arrive as plainfile://URIs, which other apps cannot open. Set it if the captured pages will be shared onward.singleShot—trueto close the camera after the first captured page instead of letting the user capture several.shotOnTap— whether a tap on the preview captures a page (trueby default).autoShotMode— the conditions for automatic capture, combined fromFrameObserver.Observationflags. The default captures when a stable document outline has been found (CUTOUT_READY) and the device is held steady (DEVICE_READY).
Receiving the Results
The callback receives a List<Uri> — one URI per captured page, in capture order. With singleShot the list holds at most one entry. If the user leaves the camera without capturing (the back button, or a cancelled launch), the list is empty; there is no error to handle. Whether the URIs are content:// or file:// follows the authority parameter above.
What the User Sees
The camera screen guides the user with a coloured frame around the detected document:
- No frame — the camera is still searching for a document.
- Yellow — a document is detected and being framed; the user should hold steady.
- Green — automatic capture is imminent; the conditions in
autoShotModeare met. - Red — shown briefly while the shot is being taken.
The colours follow the same convention as the iOS SDK’s smart camera, so users of an application shipping on both platforms get the same signals. Short hint texts in the on-screen console (“Looking for document” and similar) accompany the frame.
Testing Against the Camera Screen
The camera screen is the SDK’s own UI, so an automated UI test cannot address its controls the way it would address yours. One control carries a supported contract for that purpose: the shutter button publishes the content description Take photo, and its enabled state is the SDK’s readiness signal.
val shutter = device.wait(Until.findObject(By.desc("Take photo")), 15_000)
check(shutter != null && shutter.isEnabled) { "the camera is not ready to capture" }
shutter.click()
Two things this lets a test do that it otherwise cannot. It can wait for readiness rather than guess at it — the camera takes a moment to bind, and a shot requested before then is discarded rather than queued, so a test that taps too early sees nothing happen and no error. And it can address the control without depending on the SDK’s internal view tree, whose resource identifiers carry no compatibility promise.
What the enabled state means precisely: the camera is bound and a shot will be accepted. It does not promise the preview is already delivering frames, nor that a document has been detected. A test that needs those should wait on what it actually needs.
The content description is also the control’s accessibility label, so it is what a screen reader announces.
Permissions and Hardware
The library’s manifest declares the camera permission, and manifest merging carries it into your application — you declare nothing. At runtime the camera screen requests the permission itself the first time it opens.
The library also marks the android.hardware.camera feature as required, which means app stores hide an application that includes the camera module from devices without a camera. If your application must remain installable on such devices, override the <uses-feature> requirement in your own manifest and launch the camera only after checking availability.
Detection quality follows the camera stream: an autofocus rear camera is expected, and a sharp, well-lit preview finds document edges faster. If the device has no usable back camera, or the camera fails to start, the screen stays up, reports “Camera is not available” in its console, and returns the standard cancelled result when the user leaves.
Recipe: Capture a Single Page
For flows that need exactly one page — attaching one document to a form, for example — configure the launch to close after the first capture:
cameraLauncher.launch(
CameraContract.CameraParams(
singleShot = true,
)
)
The result list then holds one URI after a capture, or none if the user backed out.