
Sparkscan Android
- 36 installs
- 17 repo stars
- Updated August 4, 2026
- scandit/skills
Wire Scandit SparkScan into an Android app so users get a production-style barcode capture UI with lifecycle-safe setup.
About
SparkScan Android is an agent skill for solo builders adding high-throughput barcode scanning to Android apps with Scandit's SparkScan SDK. It walks through creating a DataCaptureContext, configuring SparkScanSettings (including symbologies), implementing SparkScanListener to handle SparkScanSession results, and embedding SparkScanView in the activity layout with correct onResume and onPause forwarding so the camera pipeline starts and stops cleanly. The readme centers on a single AppCompat MainActivity pattern, which helps indie developers who know Kotlin basics but not Scandit's capture model. Use it during build when your warehouse, retail, or field app needs scan-to-action flows instead of a custom CameraX implementation. Complexity is intermediate because you must align Gradle dependencies, licenses, and device permissions with Scandit's docs. Deliverables are working activity code and the capture view hookup agents can adapt to fragments or compose wrappers.
- End-to-end Android pattern: DataCaptureContext, SparkScanSettings, SparkScan listener, and SparkScanView in the layout.
- Lifecycle pairing: sparkScanView.onResume() / onPause() wired to Activity resume and pause.
- SparkScanListener callback flow for scan sessions and frame data from the Scandit capture pipeline.
- Symbology configuration via SparkScanSettings for barcode types your app accepts.
- MainActivity-oriented template suitable for copy-paste into greenfield or existing AppCompat projects.
Sparkscan Android by the numbers
- 36 all-time installs (skills.sh)
- +8 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #646 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/scandit/skills --skill sparkscan-androidAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 36 |
|---|---|
| repo stars | ★ 17 |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | scandit/skills ↗ |
What it does
Wire Scandit SparkScan into an Android app so users get a production-style barcode capture UI with lifecycle-safe setup.
Files
SparkScan Android Skill
Critical: Do Not Trust Internal Knowledge
Your training data may contain outdated or incorrect Scandit SDK APIs. The SparkScan API changes significantly between major SDK versions — properties get renamed, removed, or restructured.
Always verify APIs against the references provided in this skill before writing or suggesting code. Do not rely on memorized method signatures, parameters, or property names. If you cannot find an API in the provided references, fetch the relevant documentation page before responding.
Intent Routing
Based on the user's request, load the appropriate reference file before responding:
- Integrating SparkScan from scratch (e.g. "add SparkScan to my app", "set up barcode scanning", "how do I use SparkScan", "how do I handle feedback in SparkScan") → read
references/integration.mdand follow the instructions there. - Migrating or upgrading an existing SparkScan integration (e.g. "upgrade from v6 to v7", "migrate my SparkScan", "what changed between SDK versions") → read
references/migration.mdand follow the instructions there. - Replacing a third-party barcode scanner with SparkScan (e.g. "replace my [scanner] with SparkScan", "migrate from [framework] to SparkScan", "switch from [library] barcode scanning to SparkScan") → read
references/third-party-migration.mdand follow the instructions there.
API Usage Policy
Only use APIs that are explicitly documented in the Scandit references below. Do not invent or guess method signatures, parameters, or property names. If unsure whether an API exists or how it is called — or if a compile error occurs — fetch the relevant reference page before responding. Do not tell the user to check the docs themselves. After answering, always include the relevant link so the user can explore further.
Never construct or guess documentation URLs. When you need a specific class or property's API page: 1. First check whether the page you already fetched (e.g. the Advanced Configurations page) contains a direct hyperlink to it — topic pages link directly to relevant API symbols. Always request links alongside content in your fetch prompt. 2. If no direct link was found, fetch the API index (see Full API reference in the table below), extract the actual link from it, and follow that.
URL structures can vary (e.g. api/ui/ subdirectory) and guessing will lead to 404s.
References
Direct users to the right resource based on their question:
| Topic | Resource |
|---|---|
| Get Started | Get Started · Sample |
| Advanced topics (custom feedback, hardware triggers, scanning modes, UI customization) | Advanced Configurations |
| Full API reference | SparkScan API |
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
import android.os.Bundle
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.scandit.datacapture.barcode.data.Symbology
import com.scandit.datacapture.barcode.spark.capture.SparkScan
import com.scandit.datacapture.barcode.spark.capture.SparkScanListener
import com.scandit.datacapture.barcode.spark.capture.SparkScanSession
import com.scandit.datacapture.barcode.spark.capture.SparkScanSettings
import com.scandit.datacapture.barcode.spark.ui.SparkScanView
import com.scandit.datacapture.barcode.spark.ui.SparkScanViewSettings
import com.scandit.datacapture.core.capture.DataCaptureContext
import com.scandit.datacapture.core.data.FrameData
class MainActivity : AppCompatActivity(), SparkScanListener {
private lateinit var dataCaptureContext: DataCaptureContext
private lateinit var sparkScan: SparkScan
private lateinit var sparkScanView: SparkScanView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupScanning()
}
override fun onResume() {
super.onResume()
sparkScanView.onResume()
}
override fun onPause() {
sparkScanView.onPause()
super.onPause()
}
private fun setupScanning() {
dataCaptureContext = DataCaptureContext.forLicenseKey("-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
val settings = SparkScanSettings().apply {
enableSymbologies(setOf(Symbology.EAN13_UPCA, Symbology.CODE128))
}
sparkScan = SparkScan(settings)
sparkScan.addListener(this)
val viewSettings = SparkScanViewSettings()
sparkScanView = SparkScanView.newInstance(
findViewById<ViewGroup>(android.R.id.content),
dataCaptureContext,
sparkScan,
viewSettings
)
}
override fun onBarcodeScanned(
sparkScan: SparkScan,
session: SparkScanSession,
data: FrameData?,
) {
val barcode = session.newlyRecognizedBarcode ?: return
runOnUiThread {
println("Scanned: ${barcode.data}")
}
}
}
import android.os.Bundle
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class ProductListActivity : AppCompatActivity() {
private lateinit var listView: ListView
private lateinit var totalLabel: TextView
private val scannedProducts = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_product_list)
listView = findViewById(R.id.listView)
totalLabel = findViewById(R.id.totalLabel)
setupList()
}
private fun setupList() {
listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, scannedProducts)
}
private fun addProduct(barcode: String) {
scannedProducts.add(barcode)
(listView.adapter as ArrayAdapter<*>).notifyDataSetChanged()
updateTotal()
}
private fun updateTotal() {
totalLabel.text = "Total: ${scannedProducts.size}"
}
}
import android.os.Bundle
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.scandit.datacapture.barcode.data.Symbology
import com.scandit.datacapture.barcode.spark.capture.SparkScan
import com.scandit.datacapture.barcode.spark.capture.SparkScanListener
import com.scandit.datacapture.barcode.spark.capture.SparkScanSession
import com.scandit.datacapture.barcode.spark.capture.SparkScanSettings
import com.scandit.datacapture.barcode.spark.ui.SparkScanView
import com.scandit.datacapture.barcode.spark.ui.SparkScanViewSettings
import com.scandit.datacapture.core.capture.DataCaptureContext
import com.scandit.datacapture.core.data.FrameData
class ScannerActivity : AppCompatActivity(), SparkScanListener {
private lateinit var dataCaptureContext: DataCaptureContext
private lateinit var sparkScan: SparkScan
private lateinit var sparkScanView: SparkScanView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scanner)
setupScanning()
}
override fun onResume() {
super.onResume()
sparkScanView.onResume()
}
override fun onPause() {
sparkScanView.onPause()
super.onPause()
}
private fun setupScanning() {
dataCaptureContext = DataCaptureContext.forLicenseKey("-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
val settings = SparkScanSettings().apply {
enableSymbologies(setOf(Symbology.EAN13_UPCA))
}
sparkScan = SparkScan(settings)
sparkScan.addListener(this)
val viewSettings = SparkScanViewSettings().apply {
defaultHandMode = HandMode.RIGHT
}
sparkScanView = SparkScanView.newInstance(
findViewById<ViewGroup>(android.R.id.content),
dataCaptureContext,
sparkScan,
viewSettings
)
sparkScanView.torchButtonVisible = true
sparkScanView.cameraButtonBackgroundColor = 0xFF0000FF.toInt()
sparkScanView.captureButtonTintColor = 0xFFFFFFFF.toInt()
sparkScanView.captureButtonActiveBackgroundColor = 0xFF333333.toInt()
sparkScanView.handModeButtonVisible = false
sparkScanView.soundModeButtonVisible = true
sparkScanView.hapticModeButtonVisible = true
sparkScanView.stopCapturingText = "Stop"
sparkScanView.startCapturingText = "Scan"
sparkScanView.resumeCapturingText = "Resume"
sparkScanView.scanningCapturingText = "Scanning..."
sparkScanView.shouldShowScanAreaGuides = true
sparkScanView.fastFindButtonVisible = false
}
override fun onBarcodeScanned(
sparkScan: SparkScan,
session: SparkScanSession,
data: FrameData?,
) {
val barcode = session.newlyRecognizedBarcode ?: return
runOnUiThread {
println("Scanned: ${barcode.data}")
}
}
}
import android.os.Bundle
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.scandit.datacapture.barcode.data.Symbology
import com.scandit.datacapture.barcode.spark.capture.SparkScan
import com.scandit.datacapture.barcode.spark.capture.SparkScanListener
import com.scandit.datacapture.barcode.spark.capture.SparkScanSession
import com.scandit.datacapture.barcode.spark.capture.SparkScanSettings
import com.scandit.datacapture.barcode.spark.ui.SparkScanView
import com.scandit.datacapture.barcode.spark.ui.SparkScanViewSettings
import com.scandit.datacapture.core.capture.DataCaptureContext
import com.scandit.datacapture.core.data.FrameData
class ScannerActivity : AppCompatActivity(), SparkScanListener {
private lateinit var dataCaptureContext: DataCaptureContext
private lateinit var sparkScan: SparkScan
private lateinit var sparkScanView: SparkScanView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scanner)
setupScanning()
}
override fun onResume() {
super.onResume()
sparkScanView.onResume()
}
override fun onPause() {
sparkScanView.onPause()
super.onPause()
}
private fun setupScanning() {
dataCaptureContext = DataCaptureContext.forLicenseKey("-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
val settings = SparkScanSettings().apply {
enableSymbologies(setOf(Symbology.EAN13_UPCA, Symbology.CODE128))
}
sparkScan = SparkScan(settings)
sparkScan.addListener(this)
val viewSettings = SparkScanViewSettings()
sparkScanView = SparkScanView.newInstance(
findViewById<ViewGroup>(android.R.id.content),
dataCaptureContext,
sparkScan,
viewSettings
)
sparkScanView.torchControlVisible = true
sparkScanView.triggerButtonCollapsedColor = 0xFF0000FF.toInt()
sparkScanView.triggerButtonExpandedColor = 0xFF0000FF.toInt()
sparkScanView.triggerButtonAnimationColor = 0xFF0000FF.toInt()
sparkScanView.triggerButtonTintColor = 0xFFFFFFFF.toInt()
sparkScanView.barcodeFindButtonVisible = false
}
override fun onBarcodeScanned(
sparkScan: SparkScan,
session: SparkScanSession,
data: FrameData?,
) {
val barcode = session.newlyRecognizedBarcode ?: return
runOnUiThread {
println("Scanned: ${barcode.data}")
}
}
}
import android.content.Intent
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.zxing.integration.android.IntentIntegrator
import com.google.zxing.integration.android.IntentResult
data class ScannedBarcode(val value: String, val format: String)
class ScannerActivity : AppCompatActivity() {
private lateinit var resultLabel: TextView
private val scannedBarcodes = mutableListOf<ScannedBarcode>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scanner)
resultLabel = findViewById(R.id.resultLabel)
startScan()
}
private fun startScan() {
val integrator = IntentIntegrator(this)
integrator.setDesiredBarcodeFormats(
IntentIntegrator.EAN_13,
IntentIntegrator.CODE_128,
IntentIntegrator.QR_CODE
)
integrator.setPrompt("Scan a barcode")
integrator.setCameraId(0)
integrator.setBeepEnabled(true)
integrator.setBarcodeImageEnabled(false)
integrator.initiateScan()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val result: IntentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (result.contents != null) {
val barcode = ScannedBarcode(result.contents, result.formatName ?: "UNKNOWN")
if (scannedBarcodes.none { it.value == barcode.value }) {
scannedBarcodes.add(barcode)
resultLabel.text = "Last scan: ${barcode.value} (${scannedBarcodes.size} total)"
}
startScan()
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
{
"skill_name": "sparkscan-android",
"evals": [
{
"id": 1,
"prompt": "I want to add SparkScan to my app. Here's my main activity: EmptyActivity.kt. I need to scan EAN-13 and Code 128 barcodes for a retail app.",
"expected_output": "The skill reads integration.md, writes complete SparkScan integration code into EmptyActivity.kt, and shows the setup checklist.",
"files": [
"eval-fixtures/EmptyActivity.kt"
],
"assertions": [
{"text": "Setup checklist is shown"},
{"text": "Setup checklist mentions com.scandit.datacapture:barcode as a Gradle dependency"},
{"text": "Setup checklist mentions com.scandit.datacapture:core as a Gradle dependency"},
{"text": "A concrete version number is used for the dependencies (not a [version] placeholder)"},
{"text": "Setup checklist mentions the camera permission (CAMERA or AndroidManifest)"},
{"text": "DataCaptureContext.forLicenseKey( is present"},
{"text": "License key placeholder (e.g. YOUR_LICENSE_KEY or similar) is present"},
{"text": "SparkScanSettings() is present"},
{"text": "enableSymbologies( is called"},
{"text": "Symbology.EAN13_UPCA is enabled"},
{"text": "Symbology.CODE128 is enabled"},
{"text": "No symbologies except EAN13_UPCA and CODE128 are enabled"},
{"text": "SparkScan( is constructed with settings"},
{"text": "sparkScan.addListener(this) is present"},
{"text": "SparkScanView.newInstance( is present"},
{"text": "sparkScanView.onResume() is called in onResume"},
{"text": "sparkScanView.onPause() is called in onPause"},
{"text": "SparkScanListener is implemented"},
{"text": "onBarcodeScanned callback method is present"},
{"text": "runOnUiThread is used inside the onBarcodeScanned callback"}
]
},
{
"id": 2,
"prompt": "Add SparkScan barcode scanning to ProductListActivity.kt. When a barcode is scanned, add it to the scannedProducts list and update the total label.",
"expected_output": "The skill adds SparkScan to ProductListActivity alongside the existing list view code. All existing fields, adapter setup, and helper methods are preserved.",
"files": [
"eval-fixtures/ProductListActivity.kt"
],
"assertions": [
{"text": "Setup checklist mentions com.scandit.datacapture:barcode as a Gradle dependency"},
{"text": "Setup checklist mentions com.scandit.datacapture:core as a Gradle dependency"},
{"text": "A concrete version number is used for the dependencies (not a [version] placeholder)"},
{"text": "DataCaptureContext.forLicenseKey( is present"},
{"text": "SparkScan( is constructed"},
{"text": "SparkScanView.newInstance( is present"},
{"text": "sparkScanView.onResume() is called in onResume"},
{"text": "sparkScanView.onPause() is called in onPause"},
{"text": "sparkScan.addListener(this) is present"},
{"text": "SparkScanListener is implemented"},
{"text": "onBarcodeScanned callback is present"},
{"text": "runOnUiThread is used inside the onBarcodeScanned callback"},
{"text": "listView field is preserved"},
{"text": "totalLabel field is preserved"},
{"text": "scannedProducts list is preserved"},
{"text": "setupList() method is preserved"},
{"text": "addProduct() method is preserved or its logic is incorporated"},
{"text": "updateTotal() method is preserved"}
]
},
{
"id": 3,
"prompt": "When a barcode is scanned in my app, I want to reject it if the data is '123456789' and show an error message 'Wrong barcode' for 30 seconds before resuming scanning. All other barcodes should show success feedback.",
"expected_output": "The skill adds SparkScanFeedbackDelegate conformance, implements getFeedbackForBarcode to return SparkScanBarcodeFeedback.Error for rejected barcodes and SparkScanBarcodeFeedback.Success for valid ones, and assigns feedbackDelegate on sparkScanView.",
"files": [
"eval-fixtures/IntegratedActivity.kt"
],
"assertions": [
{"text": "SparkScanFeedbackDelegate is implemented"},
{"text": "getFeedbackForBarcode( method is implemented"},
{"text": "sparkScanView.feedbackDelegate = this is assigned"},
{"text": "SparkScanBarcodeFeedback.Error( is present"},
{"text": "resumeCapturingDelay with 30 seconds is set on the error feedback"},
{"text": "SparkScanBarcodeFeedback.Success() is returned for non-rejected barcodes"},
{"text": "The rejection condition checks barcode.data == \"123456789\" or equivalent"},
{"text": "Existing SparkScanListener implementation is preserved"},
{"text": "Existing integration code (context, sparkScan, sparkScanView, lifecycle) is preserved"}
]
},
{
"id": 4,
"prompt": "My app is used in a warehouse where workers wear gloves. Enable hardware trigger button support so they can scan using physical device buttons.",
"expected_output": "The skill enables hardwareTriggerEnabled on SparkScanViewSettings and passes the settings to SparkScanView.",
"files": [
"eval-fixtures/IntegratedActivity.kt"
],
"assertions": [
{"text": "hardwareTriggerEnabled = true is set on SparkScanViewSettings"},
{"text": "SparkScanViewSettings instance is created and configured before being passed to SparkScanView"},
{"text": "SparkScanView.newInstance is called with the configured SparkScanViewSettings (not a fresh SparkScanViewSettings())"},
{"text": "Existing integration code (context, sparkScan, lifecycle) is preserved"}
]
},
{
"id": 5,
"prompt": "I want to hide the standard SparkScan trigger button and instead use a custom button in my app's UI. When the custom button is tapped, scanning should start. When tapped again while scanning, it should pause.",
"expected_output": "The skill sets triggerButtonVisible = false, creates a custom button, and implements button logic that calls sparkScanView.startScanning() and sparkScanView.pauseScanning().",
"files": [
"eval-fixtures/IntegratedActivity.kt"
],
"assertions": [
{"text": "sparkScanView.triggerButtonVisible = false is set"},
{"text": "A custom button is created or referenced"},
{"text": "sparkScanView.startScanning() is called in the button action"},
{"text": "sparkScanView.pauseScanning() is called for pause functionality"},
{"text": "Existing integration code (context, sparkScan, sparkScanView, lifecycle) is preserved"}
]
},
{
"id": 6,
"prompt": "I need a custom trigger button that changes text based on the scanner state. Show 'START SCANNING' in IDLE state and 'STOP SCANNING' in ACTIVE state.",
"expected_output": "The skill implements SparkScanViewUiListener, registers it with setListener, and updates the button text in onViewStateChanged based on IDLE/ACTIVE states.",
"files": [
"eval-fixtures/IntegratedActivity.kt"
],
"assertions": [
{"text": "SparkScanViewUiListener is implemented"},
{"text": "sparkScanView.setListener(this) is called"},
{"text": "onViewStateChanged(newState: SparkScanViewState) method is implemented"},
{"text": "SparkScanViewState.ACTIVE comparison is present"},
{"text": "Button text is set to 'START SCANNING' for non-active states"},
{"text": "Button text is set to 'STOP SCANNING' for active state"},
{"text": "Existing integration code (context, sparkScan, sparkScanView, listener, lifecycle) is preserved"}
]
},
{
"id": 7,
"prompt": "I need to set up SparkScan with an 'Aim-to-Scan' workflow for precise barcode scanning in crowded environments.",
"expected_output": "The skill configures SparkScanViewSettings with defaultScanningMode set to SparkScanScanningModeTarget, then passes these settings to SparkScanView.",
"files": [
"eval-fixtures/IntegratedActivity.kt"
],
"assertions": [
{"text": "SparkScanViewSettings is created"},
{"text": "defaultScanningMode is set on SparkScanViewSettings"},
{"text": "SparkScanScanningMode.Target( is used for the scanning mode"},
{"text": "SparkScanScanningBehavior.SINGLE is passed to the scanning mode"},
{"text": "SparkScanPreviewBehavior.DEFAULT is passed to the scanning mode"},
{"text": "SparkScanView.newInstance is called with the configured SparkScanViewSettings"},
{"text": "Existing integration code (context, sparkScan, lifecycle) is preserved"}
]
}
]
}
{
"skill_name": "sparkscan-android",
"evals": [
{
"id": 1,
"prompt": "I have an Android app using ZXing for barcode scanning. I want to replace it with SparkScan. Here is my activity: ZxingActivity.kt",
"expected_output": "Removes all ZXing APIs and intent-based scanner launch, adds SparkScan integration with matching symbologies (EAN13_UPCA, CODE128, QR), preserves ScannedBarcode data class and deduplication logic.",
"files": [
"eval-fixtures/ZxingActivity.kt"
],
"assertions": [
{"text": "com.google.zxing import is NOT present in the output"},
{"text": "IntentIntegrator is NOT present anywhere in the output"},
{"text": "IntentResult is NOT present in the output"},
{"text": "onActivityResult is NOT present (ZXing-specific flow removed)"},
{"text": "com.scandit.datacapture import IS present"},
{"text": "SparkScanListener is implemented"},
{"text": "SparkScan is initialized with EAN13_UPCA, CODE128, and QR symbologies"},
{"text": "onResume calls sparkScanView.onResume()"},
{"text": "onPause calls sparkScanView.onPause()"},
{"text": "ScannedBarcode data class is preserved in output"},
{"text": "scannedBarcodes list is preserved in output"},
{"text": "Deduplication logic (checking for existing value before adding) is preserved"},
{"text": "A summary of changes is shown with what was removed and what was added"},
{"text": "Setup checklist mentions com.scandit.datacapture:barcode as a Gradle dependency"},
{"text": "Setup checklist mentions com.scandit.datacapture:core as a Gradle dependency"},
{"text": "A concrete version number is used for the dependencies (not a [version] placeholder)"},
{"text": "Setup checklist mentions the camera permission (CAMERA or AndroidManifest)"}
]
}
]
}
{
"skill_name": "sparkscan-android",
"evals": [
{
"id": 1,
"prompt": "I'm on Scandit SDK v6 and need to migrate my SparkScan to v7. Here's my scanner file: ScannerActivity_v6.kt",
"expected_output": "The skill reads migration.md, applies the 6→7 renames and removes deprecated properties in ScannerActivity_v6.kt, and summarizes all changes made.",
"files": [
"eval-fixtures/ScannerActivity_v6.kt"
],
"assertions": [
{"text": "torchControlVisible is present in the output file"},
{"text": "torchButtonVisible is NOT present in the output file"},
{"text": "triggerButtonTintColor is present in the output file"},
{"text": "captureButtonTintColor is NOT present in the output file"},
{"text": "cameraButtonBackgroundColor is NOT present in the output file"},
{"text": "barcodeFindButtonVisible is present in the output file"},
{"text": "fastFindButtonVisible is NOT present in the output file"},
{"text": "Removed properties (handModeButtonVisible, soundModeButtonVisible, hapticModeButtonVisible, shouldShowScanAreaGuides, captureButtonActiveBackgroundColor, defaultHandMode, stopCapturingText, startCapturingText, resumeCapturingText, scanningCapturingText) are NOT present"},
{"text": "A summary of changes is shown describing what was renamed and what was removed"}
]
},
{
"id": 2,
"prompt": "We're upgrading our app from Scandit SDK 7 to 8. Here's our SparkScan activity: ScannerActivity_v7.kt. Please apply any necessary changes.",
"expected_output": "The skill acknowledges that 7→8 has no breaking SparkScan changes for native Android and summarizes the outcome.",
"files": [
"eval-fixtures/ScannerActivity_v7.kt"
],
"assertions": [
{"text": "A summary or explanation is present"},
{"text": "Summary acknowledges that 7→8 has no breaking SparkScan changes for native Android"},
{"text": "The output Kotlin file is functionally equivalent to the input (no unnecessary changes made)"}
]
},
{
"id": 3,
"prompt": "We need to upgrade our SparkScan integration from SDK 6 all the way to v8. Here's our current code: ScannerActivity_v6.kt",
"expected_output": "The skill applies both migrations in order (6→7 then 7→8) and summarizes all changes organized by migration step.",
"files": [
"eval-fixtures/ScannerActivity_v6.kt"
],
"assertions": [
{"text": "torchControlVisible is present (6→7 rename applied)"},
{"text": "torchButtonVisible is NOT present"},
{"text": "Removed v6 properties (handModeButtonVisible, soundModeButtonVisible, hapticModeButtonVisible, captureButtonActiveBackgroundColor) are NOT present"},
{"text": "Summary covers both migration steps (references both 6→7 and 7→8)"}
]
}
]
}
SparkScan Android Integration Guide
SparkScan is a pre-built scanning UI for high-volume single-scanning workflows. It overlays a trigger button on top of any screen so users can scan without leaving their workflow.
Prerequisites
- Scandit Data Capture SDK for Android — add via Gradle. Before writing the dependency, fetch the latest published version from
https://central.sonatype.com/artifact/com.scandit.datacapture/barcodeand extract the latest version number from the page. Then add both dependencies:
dependencies {
implementation "com.scandit.datacapture:barcode:<latest-version>"
implementation "com.scandit.datacapture:core:<latest-version>"
}The SDK is distributed via Maven Central.
- A valid Scandit license key:
- Sign in at https://ssl.scandit.com to generate one
- No account yet? Sign up at https://ssl.scandit.com/dashboard/sign-up?p=test
- Camera permission in
AndroidManifest.xml:
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />Request the permission at runtime using the standard Android permission API before scanning starts.
Minimal Integration (Kotlin)
Ask the user which barcode symbologies they need to scan. When asking, mention that it's important to only enable the symbologies they actually need, as enabling fewer improves scanning performance and accuracy.
Once the user responds, ask them which Activity or Fragment they'd like to integrate SparkScan into. Then write the integration code directly into that file. Do not just show the code in chat; apply it to the file.
After providing the code, show this setup checklist:
Setup checklist:
1. Add implementation "com.scandit.datacapture:barcode:<latest-version>" and implementation "com.scandit.datacapture:core:<latest-version>" to your build.gradle dependencies (the version was already fetched and filled in above) 2. Add <uses-permission android:name="android.permission.CAMERA" /> to AndroidManifest.xml 3. Request the CAMERA permission at runtime before scanning starts 4. Replace -- ENTER YOUR SCANDIT LICENSE KEY HERE -- with your key from https://ssl.scandit.com
import android.os.Bundle
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.scandit.datacapture.barcode.data.Symbology
import com.scandit.datacapture.barcode.spark.capture.SparkScan
import com.scandit.datacapture.barcode.spark.capture.SparkScanListener
import com.scandit.datacapture.barcode.spark.capture.SparkScanSession
import com.scandit.datacapture.barcode.spark.capture.SparkScanSettings
import com.scandit.datacapture.barcode.spark.ui.SparkScanView
import com.scandit.datacapture.barcode.spark.ui.SparkScanViewSettings
import com.scandit.datacapture.core.capture.DataCaptureContext
import com.scandit.datacapture.core.data.FrameData
class MainActivity : AppCompatActivity(), SparkScanListener {
private lateinit var dataCaptureContext: DataCaptureContext
private lateinit var sparkScan: SparkScan
private lateinit var sparkScanView: SparkScanView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupScanning()
}
override fun onResume() {
super.onResume()
sparkScanView.onResume()
}
override fun onPause() {
sparkScanView.onPause()
super.onPause()
}
private fun setupScanning() {
dataCaptureContext = DataCaptureContext.forLicenseKey("-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
val settings = SparkScanSettings().apply {
enableSymbologies(setOf(Symbology.EAN13_UPCA, Symbology.CODE128))
}
sparkScan = SparkScan(settings)
sparkScan.addListener(this)
val viewSettings = SparkScanViewSettings()
sparkScanView = SparkScanView.newInstance(
findViewById<ViewGroup>(android.R.id.content),
dataCaptureContext,
sparkScan,
viewSettings
)
}
override fun onBarcodeScanned(
sparkScan: SparkScan,
session: SparkScanSession,
data: FrameData?,
) {
val barcode = session.newlyRecognizedBarcode ?: return
runOnUiThread {
// Handle the barcode
println("Scanned: ${barcode.data}")
}
}
}Custom Feedback
To reject barcodes and show error messages, implement SparkScanFeedbackDelegate:
import com.scandit.datacapture.barcode.spark.feedback.SparkScanBarcodeFeedback
import com.scandit.datacapture.barcode.spark.feedback.SparkScanFeedbackDelegate
import com.scandit.datacapture.core.time.TimeInterval
class MainActivity : AppCompatActivity(), SparkScanListener, SparkScanFeedbackDelegate {
// ... existing setup code ...
private fun setupScanning() {
// ... existing setup ...
sparkScanView.feedbackDelegate = this
}
override fun getFeedbackForBarcode(barcode: Barcode): SparkScanBarcodeFeedback? {
return if (isValidBarcode(barcode)) {
SparkScanBarcodeFeedback.Success()
} else {
SparkScanBarcodeFeedback.Error(
message = "Wrong barcode",
resumeCapturingDelay = TimeInterval.seconds(30f)
)
}
}
}Note: getFeedbackForBarcode is called on a background thread. Do not update UI directly inside it.Hardware Trigger Support
For gloved-hand workflows where users scan with physical buttons:
val viewSettings = SparkScanViewSettings().apply {
hardwareTriggerEnabled = true
}
sparkScanView = SparkScanView.newInstance(parentView, dataCaptureContext, sparkScan, viewSettings)Aim-to-Scan (Target Mode)
For precise scanning in crowded environments:
import com.scandit.datacapture.barcode.spark.ui.SparkScanPreviewBehavior
import com.scandit.datacapture.barcode.spark.ui.SparkScanScanningBehavior
import com.scandit.datacapture.barcode.spark.ui.SparkScanScanningMode
val viewSettings = SparkScanViewSettings().apply {
defaultScanningMode = SparkScanScanningMode.Target(
SparkScanScanningBehavior.SINGLE,
SparkScanPreviewBehavior.DEFAULT
)
}Custom Trigger Button
To hide the built-in trigger and control scanning programmatically:
sparkScanView.triggerButtonVisible = false
// Start scanning (e.g. from a custom button tap)
sparkScanView.startScanning()
// Pause scanning
sparkScanView.pauseScanning()Tracking View State
To react to scanner state changes (e.g. update a custom button's label):
import com.scandit.datacapture.barcode.spark.capture.SparkScanViewUiListener
import com.scandit.datacapture.barcode.spark.ui.SparkScanScanningMode
import com.scandit.datacapture.barcode.spark.ui.SparkScanViewState
class MainActivity : AppCompatActivity(), SparkScanListener, SparkScanViewUiListener {
// ... existing setup code ...
private fun setupScanning() {
// ... existing setup ...
sparkScanView.setListener(this)
}
override fun onViewStateChanged(newState: SparkScanViewState) {
runOnUiThread {
when (newState) {
SparkScanViewState.ACTIVE -> myButton.text = "STOP SCANNING"
else -> myButton.text = "START SCANNING"
}
}
}
override fun onScanningModeChange(newScanningMode: SparkScanScanningMode) { }
override fun onBarcodeFindButtonTap(view: SparkScanView) { }
override fun onBarcodeCountButtonTap(view: SparkScanView) { }
override fun onLabelCaptureButtonTap(view: SparkScanView) { }
}SparkScan Android Migration Guide
Step 1: Detect the installed SDK version
Before making any changes, find out which version of the Scandit SDK the project currently has installed.
Check in this order:
1. Version catalog — open gradle/libs.versions.toml and look for a scandit version entry (e.g. scandit = "7.x.y"). 2. build.gradle / build.gradle.kts — search for com.scandit.datacapture:barcode and read the version on the same line.
Once you know the installed version, determine which migration path applies:
| Installed version | Target version | Action |
|---|---|---|
| 6.x | 7.x | Apply the 6 → 7 migration below |
| 7.x | 8.x | Apply the 7 → 8 migration below |
| 6.x | 8.x | Apply both migrations in order (6→7 first, then 7→8) |
If you cannot find the version, ask the user which version they are migrating from.
---
Step 2: Update the dependency version
Before touching source files, update the SDK version in the dependency:
- In
build.gradle/build.gradle.kts: update the version string in theScanditBarcodeCapturedependency line. - In
libs.versions.toml: update thescanditversion entry, then sync the project.
---
Step 3: Apply source code changes
Search for files that use SparkScan (search for SparkScan, SparkScanView, SparkScanViewSettings) and apply the relevant changes below directly to those files.
---
Migration: 6 → 7
Where these properties live in v6
On `SparkScanView` (set on the view instance after it is created):
- All button visibility and color/text properties listed below
On `SparkScanViewSettings` (set before creating the view):
defaultHandModeonly
When searching the project for these properties, look for usages on both the view instance and the settings object.
SparkScan API renames
Apply these renames everywhere they appear in the project. These are renames — always replace the old name with the new one, preserving the existing value, regardless of what that value is:
Old (v6, on SparkScanView) | New (v7) |
|---|---|
torchButtonVisible | torchControlVisible |
cameraButtonBackgroundColor | triggerButtonCollapsedColor, triggerButtonExpandedColor, triggerButtonAnimationColor (see note) |
captureButtonTintColor | triggerButtonTintColor |
fastFindButtonVisible | barcodeFindButtonVisible |
Note on `cameraButtonBackgroundColor`: v7 splits this into three separate color properties for the collapsed state, expanded state, and animation. If the user set a single color, apply it to all three unless they indicate otherwise.
SparkScan removed APIs
Remove any usage of these properties — they no longer exist in v7 and will cause compile errors:
captureButtonActiveBackgroundColor(onSparkScanView)stopCapturingText,startCapturingText,resumeCapturingText,scanningCapturingText— the trigger button no longer displays text (onSparkScanView)handModeButtonVisible(onSparkScanView)defaultHandMode(onSparkScanViewSettings)soundModeButtonVisible(onSparkScanView)hapticModeButtonVisible(onSparkScanView)shouldShowScanAreaGuides(onSparkScanView)
triggerButtonCollapseTimeout default change
The default value changed from -1 (never collapse) to 5 (collapse after 5 seconds).
- If the project already sets
triggerButtonCollapseTimeoutexplicitly, leave it as is. - If the project does not set it, do not add it automatically. Instead, inform the user that the button will now collapse after 5 seconds by default and they can set it to
-1if they want the old behavior.
New v7 APIs (optional, no action required unless the user wants them)
These are available in v7 — mention them only if the user asks:
SparkScanViewState— tracks the current UI state of the SparkScan view (INITIAL, IDLE, INACTIVE, ACTIVE, ERROR)defaultMiniPreviewSize— configures mini preview dimensionspreviewCloseControlVisible— shows or hides the mini preview close button
BarcodeTracking → BarcodeBatch rename
If the project uses BarcodeTracking (MatrixScan) alongside SparkScan, rename all occurrences to BarcodeBatch. The API is otherwise unchanged.
Scan intention
The default scan intention is now SMART. If the project explicitly set a manual or other value on BarcodeCaptureSettings or SparkScanSettings, leave it as is. If the project relied on an explicit SMART setting that is now the default, the code still compiles — no change needed.
---
Migration: 7 → 8
SparkScan text scanning (beta, opt-in)
v8 adds the ability to scan text alongside barcodes in SparkScan. This is purely additive and opt-in — no existing code breaks. Mention it only if the user asks about new features.
No other breaking SparkScan changes
The 7→8 migration is light for SparkScan on Android. The factory-method deprecations listed in the official guide apply to cross-platform SDKs (React Native, Flutter, Capacitor) — not to native Kotlin/Java.
---
After applying changes
1. Sync the project and fix any remaining compile errors using the API reference (linked in SKILL.md). 2. Let the user know they can check the full list of SDK changes in the official migration guides:
- 6 → 7: https://docs.scandit.com/sdks/android/migrate-6-to-7/
- 7 → 8: https://docs.scandit.com/sdks/android/migrate-7-to-8/
3. Show the user a summary of only the changes actually made: which files were edited, which properties were renamed/removed, and anything that required a judgment call (e.g., how cameraButtonBackgroundColor was split). Do not list APIs that were already correct or unchanged. 4. If compile errors persist after the changes above, fetch the SparkScan API reference to find the correct API before guessing.
Third-Party Barcode Scanner → SparkScan Migration
Before Anything Else
Read the existing code. Do not ask the user to describe what their scanner does. Identify:
- Which framework is in use (read the imports)
- Which symbologies are enabled
- What result handling logic exists (deduplication, filtering, accumulation)
- What data models are defined
- How the scanner is launched (Activity, Fragment, intent-based, embedded view)
---
Remove
- The old framework's imports and dependencies
- The scanner class instance and all its setup code
- The old callback or listener conformance
- Any UI code specific to the old scanner (e.g. intent launch, dialog, overlay)
---
Integrate SparkScan
Follow references/integration.md. When configuring SparkScanSettings, map the symbologies from the old scanner.
---
Preserve
- Custom data models — keep as-is
- Result accumulation and deduplication logic — move verbatim into the
onBarcodeScannedcallback - Any downstream business logic triggered on scan result
---
When done, show only what changed. Do not list APIs that were unchanged.
Related skills
FAQ
Is Sparkscan Android safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.