Camera Theme Customization
The Paper Receipt camera scanning screen — its capture, retake, finish, confirm, cancel, and torch buttons, the scanning tooltips, the progress bar, and the receipt-edge guideline hints — is fully themeable through standard Android XML styles. You define a style that extends the SDK's BlinkRecognizerStyle, override only the brand colors and icons you care about, and hand that style to the camera through CameraCharacteristics.
Internally, the SDK reads your XML theme attributes and maps them onto a BlinkRecognizerStyle model that drives the Compose-based camera UI. That model is an SDK-internal type — you never construct it yourself. XML styling is the supported and only customization path.
Table of Contents
- How It Works
- Setup (Host App)
- The
BlinkRecognizerStyleModel - Theme Attribute Reference
- Full Example
- Tips and Pitfalls
1. How It Works
| Stage | What happens |
|---|---|
| Your style | You add a style to res/values/styles.xml that uses BlinkRecognizerStyle as its parent and overrides the attributes you want to brand. |
CameraCharacteristics | You pass the style's resource ID via CameraCharacteristics.Builder().style(...). If you omit it, the SDK uses its default style (R.style.BlinkRecognizerStyle). |
| SDK theming | The SDK applies your style to the camera screen and wraps the Compose context in it. |
| Attribute resolution | The SDK resolves each theme attribute and builds the internal BlinkRecognizerStyle model that the camera UI renders from. |
2. Setup (Host App)
Step 1 — Define your brand colors
In res/values/colors.xml, declare the colors you want the camera UI to use:
<resources>
<color name="brand_primary">#0B5FFF</color>
<color name="brand_primary_tapped">#0846BF</color>
<color name="brand_secondary">#EEF1F6</color>
<color name="brand_secondary_tapped">#C7CDD6</color>
</resources>
Step 2 — Extend BlinkRecognizerStyle
In res/values/styles.xml, create a style whose parent is the SDK's BlinkRecognizerStyle. Use dot-notation, which implies inheritance — BlinkRecognizerStyle.YourBrand automatically inherits everything from BlinkRecognizerStyle, so you only declare the attributes you want to change. Anything you leave out keeps the SDK default.
<style name="BlinkRecognizerStyle.YourBrand">
<!-- Material color roles cascade into the buttons, tooltip, and hints -->
<item name="colorPrimary">@color/brand_primary</item>
<item name="colorPrimaryVariant">@color/brand_primary_tapped</item>
<item name="colorSecondary">@color/brand_secondary</item>
<item name="colorSecondaryVariant">@color/brand_secondary_tapped</item>
<item name="colorOnPrimary">@android:color/white</item>
<!-- Camera-specific colors -->
<item name="guidelineHintViewBackground">?attr/colorPrimary</item>
<item name="progressBarColor">?attr/colorPrimary</item>
</style>
Most apps only need to override the five Material color roles. The tooltip, progress bar, guideline hints, and all six buttons derive their colors from colorPrimary / colorSecondary / colorOnPrimary, so setting those alone re-skins the entire camera screen. Override the per-component styles (next section) only when you need finer control over individual icons or buttons.
Step 3 — Pass the style to the camera
When you launch the camera through CameraRecognizerContract, set your style on the CameraCharacteristics:
import com.microblink.camera.ui.CameraCharacteristics
import com.microblink.camera.ui.CameraRecognizerContract
import com.microblink.camera.ui.CameraRecognizerOptions
import com.microblink.camera.ui.CameraRecognizerResults
val launcher = rememberLauncherForActivityResult(
contract = CameraRecognizerContract(),
) { result ->
when (result) {
is CameraRecognizerResults.Success -> scanResults = result.results
is CameraRecognizerResults.Exception -> Log.e("Scan", "Error: ${result.exception}")
CameraRecognizerResults.Cancelled -> { /* User cancelled */ }
}
}
launcher.launch(
CameraRecognizerOptions.Builder()
.options(scanOptions)
.characteristics(
CameraCharacteristics.Builder()
// Style name "BlinkRecognizerStyle.YourBrand" is referenced
// in code with the dot replaced by an underscore.
.style(R.style.BlinkRecognizerStyle_YourBrand)
.build()
)
.build()
)
A style named BlinkRecognizerStyle.YourBrand in XML is referenced from Kotlin/Java as R.style.BlinkRecognizerStyle_YourBrand — the resource compiler replaces dots with underscores. If you don't call .style(...), the camera falls back to R.style.BlinkRecognizerStyle, the SDK default.
3. The BlinkRecognizerStyle Model
Each theme attribute you set in XML maps to one property of the internal BlinkRecognizerStyle model. You don't interact with this model directly, but understanding it explains what each attribute controls:
| Model property | Controls | Themed by |
|---|---|---|
guidelineHintColor | Background of the receipt-edge guideline hint shown over the camera. | guidelineHintViewBackground |
shutterSurfaceColor | Surface that animates like a camera shutter on capture. | shutterSurfaceColor |
progressBarColor | Progress bar shown while a scan is being processed. | progressBarColor |
tooltipStyle | Informational tooltips (e.g. "Image seems blurry"). | blinkTooltipStyle |
torchButtonStyle | Flashlight / torch toggle button. | torchButtonStyle |
cancelButtonStyle | Cancel (exit) button. | cancelButtonStyle |
retakeButtonStyle | Retake-capture button. | retakeButtonStyle |
finishButtonStyle | Finish / done button. | finishButtonStyle |
captureButtonStyle | Main camera capture (shutter) button. | captureButtonStyle |
confirmButtonStyle | Confirm / "add another photo" button. | confirmButtonStyle |
4. Theme Attribute Reference
All attributes below are set as <item> entries inside your BlinkRecognizerStyle.YourBrand style (top-level colors) or inside the nested child styles it references (tooltip and buttons). Defaults are the values the SDK's base BlinkRecognizerStyle resolves to.
Top-level colors
Declared directly on your style:
| Attribute | Format | Controls | Default |
|---|---|---|---|
colorPrimary | color | Primary brand color. Drives the tooltip, progress bar, guideline hint, and the "primary" circle of the capture / finish / retake / confirm buttons. | SDK blue |
colorPrimaryVariant | color | Pressed/tapped variant of the primary color. | SDK blue (tapped) |
colorSecondary | color | Secondary surface color. Drives button container backgrounds (torch, cancel) and the "secondary" circle of action buttons. | Light grey |
colorSecondaryVariant | color | Pressed/ripple variant of the secondary color. | Dark grey |
colorOnPrimary | color | Foreground color used for icon tints on primary surfaces. | White |
guidelineHintViewBackground | color | Background of the receipt-edge guideline hint. | ?attr/colorPrimary |
progressBarColor | color | Color of the scan progress bar. | ?attr/colorPrimary |
shutterSurfaceColor | color | Color of the capture shutter animation surface. | Black |
Tooltip — blinkTooltipStyle
blinkTooltipStyle references a child style with these attributes:
| Attribute | Format | Controls | Default |
|---|---|---|---|
tooltipColor | color | Tooltip background and pointer color. | ?attr/colorPrimary |
tooltipTextColor | color | Tooltip text color. | ?attr/colorOnPrimary |
tooltipPointHeight | dimension | Height of the tooltip's triangular pointer. | 8dp |
tooltipBoxRadius | dimension | Corner radius of the tooltip body. | 6dp |
Torch button — torchButtonStyle
| Attribute | Format | Controls | Default |
|---|---|---|---|
android:colorAccent | color | Button container (background) color. | ?attr/colorSecondary |
android:colorPressedHighlight | color | Ripple color on press. | ?attr/colorSecondaryVariant |
android:padding | dimension | Padding around the icon. | 0dp |
torchOnSrc | reference | Drawable for the torch-on (activated) state. | ic_torch_on |
torchOffSrc | reference | Drawable for the torch-off (normal) state. | ic_torch |
torchOnContentDescription | string | Accessibility text for the torch-on state. | — |
torchOffContentDescription | string | Accessibility text for the torch-off state. | — |
android:tint | color | Icon tint. | White |
Cancel button — cancelButtonStyle
| Attribute | Format | Controls | Default |
|---|---|---|---|
android:colorAccent | color | Button container (background) color. | ?attr/colorSecondary |
android:colorPressedHighlight | color | Ripple color on press. | ?attr/colorSecondaryVariant |
android:padding | dimension | Padding around the icon. | 12dp |
android:src | reference | Button icon. | ic_exit |
android:contentDescription | string | Accessibility text. | @null |
android:tint | color | Icon tint. | White |
Action buttons — retakeButtonStyle, finishButtonStyle, captureButtonStyle, confirmButtonStyle
These four buttons share the same attribute shape (each renders as an inner and outer circle):
| Attribute | Format | Controls | Default |
|---|---|---|---|
android:colorActivatedHighlight | color | Primary (outer circle) container color. | ?attr/colorPrimary |
android:colorAccent | color | Secondary (inner circle) container color. | ?attr/colorSecondary |
android:colorPressedHighlight | color | Ripple color on press. | ?attr/colorSecondaryVariant |
android:src | reference | Button icon. | ic_retake / ic_confirm / ic_camera_capture / ic_add |
android:padding | dimension | Padding around the icon. | 0dp |
android:tint | color | Icon tint. | White |
android:contentDescription | string | Accessibility text. | @null |
A few legacy attributes are deprecated and will be ignored in future SDK versions:
android:backgroundon the button styles → useandroid:colorAccent,android:colorPressedHighlight, andandroid:colorActivatedHighlightinstead.android:srcon the torch button → usetorchOnSrcandtorchOffSrcinstead.android:statusBarColor/android:navigationBarColor/android:enforceStatusBarContrast/android:enforceNavigationBarContrastare no longer honored.
5. Full Example
This is a complete, working theme — the same one used by the BlinkReceipt sample app. It overrides the Material color roles plus every nested button and tooltip style. Copy it into your res/values/styles.xml, rename it to your brand, and adjust the colors and drawables.
<resources>
<style name="BlinkRecognizerStyle.BlinkReceiptApp">
<item name="colorPrimary">@android:color/holo_red_dark</item>
<item name="colorPrimaryVariant">@color/colorPrimaryDark</item>
<item name="colorSecondary">@color/colorAccent</item>
<item name="colorSecondaryVariant">@color/greenlight</item>
<item name="colorOnPrimary">@android:color/white</item>
<item name="blinkTooltipStyle">@style/Tooltip.BlinkReceiptAppStyle</item>
<item name="guidelineHintViewBackground">?attr/colorPrimary</item>
<item name="progressBarColor">?attr/colorPrimary</item>
<item name="torchButtonStyle">@style/Widget.AppCompat.ImageButton.TorchButtonStyle.BlinkReceiptAppStyle</item>
<item name="cancelButtonStyle">@style/Widget.AppCompat.ImageButton.CancelButtonStyle.BlinkReceiptAppStyle</item>
<item name="retakeButtonStyle">@style/Widget.AppCompat.ImageButton.RetakeButtonStyle.BlinkReceiptAppStyle</item>
<item name="finishButtonStyle">@style/Widget.AppCompat.ImageButton.FinishButtonStyle.BlinkReceiptAppStyle</item>
<item name="captureButtonStyle">@style/Widget.AppCompat.ImageButton.CaptureButtonStyle.BlinkReceiptAppStyle</item>
<item name="confirmButtonStyle">@style/Widget.AppCompat.ImageButton.ConfirmButtonStyle.BlinkReceiptAppStyle</item>
</style>
<style name="Tooltip.BlinkReceiptAppStyle">
<item name="tooltipPointHeight">8dp</item>
<item name="tooltipBoxRadius">6dp</item>
<item name="tooltipColor">?attr/colorPrimary</item>
<item name="tooltipTextColor">?attr/colorOnPrimary</item>
</style>
<style name="Widget.AppCompat.ImageButton.TorchButtonStyle.BlinkReceiptAppStyle">
<item name="torchOnSrc">@drawable/ic_chip_completed</item>
<item name="torchOffSrc">@drawable/ic_chip_not_found</item>
<item name="tint">@android:color/holo_orange_dark</item>
</style>
<style name="Widget.AppCompat.ImageButton.CancelButtonStyle.BlinkReceiptAppStyle">
<item name="android:contentDescription">@null</item>
<item name="android:padding">12dp</item>
<item name="android:src">@drawable/ic_exit</item>
<item name="tint">@android:color/holo_blue_bright</item>
</style>
<style name="Widget.AppCompat.ImageButton.FinishButtonStyle.BlinkReceiptAppStyle">
<item name="android:src">@drawable/ic_confirm</item>
<item name="tint">@android:color/holo_blue_dark</item>
</style>
<style name="Widget.AppCompat.ImageButton.RetakeButtonStyle.BlinkReceiptAppStyle">
<item name="android:src">@drawable/ic_retake</item>
<item name="tint">@android:color/white</item>
</style>
<style name="Widget.AppCompat.ImageButton.CaptureButtonStyle.BlinkReceiptAppStyle">
<item name="android:src">@drawable/ic_camera_capture</item>
<item name="tint">@android:color/white</item>
</style>
<style name="Widget.AppCompat.ImageButton.ConfirmButtonStyle.BlinkReceiptAppStyle">
<item name="android:src">@drawable/ic_add</item>
<item name="tint">@android:color/white</item>
</style>
</resources>
Each nested button and tooltip style also uses dot-notation inheritance, so it inherits from the SDK's base button styles and only needs to declare the icons/colors you override.
Launch the camera with this theme:
CameraCharacteristics.Builder()
.style(R.style.BlinkRecognizerStyle_BlinkReceiptApp)
.build()
6. Tips and Pitfalls
Extend the base style. Always use BlinkRecognizerStyle (or one of its Widget.AppCompat.ImageButton.* button styles / the Tooltip style) as a parent. Inheritance via dot-notation means unset attributes fall back to SDK defaults, so a missing icon or color never leaves a button blank.
Override only what you need. Setting the five Material color roles is enough to re-skin the whole screen. Reach for the per-button nested styles only when you want custom icons or per-button colors.
Dots become underscores in code. The XML style BlinkRecognizerStyle.YourBrand is R.style.BlinkRecognizerStyle_YourBrand in Kotlin/Java.
Pass the style every launch. The theme is read from CameraCharacteristics when the camera starts. There is no global "set once" call — include .style(...) on every launch you want branded. Omitting it uses the SDK default.
Avoid the deprecated attributes. As of SDK 1.9.10, use android:colorAccent / android:colorPressedHighlight / android:colorActivatedHighlight instead of android:background, and torchOnSrc / torchOffSrc instead of android:src on the torch button. See the warning above.