Skip to main content

Camera Theme Customization

The Paper Receipt camera scanning screen — its capture, retake, finish, confirm, cancel, torch, and hint buttons, the scanning tooltips, the progress bar, and the on-screen guidance 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.

Upgrading to Paper Receipt SDK 2.3.1

The camera UI was refreshed in 2.3.1. Themes written for earlier versions still compile and still apply, but the buttons and tooltip now take their colors from their own per-component styles instead of from your theme's Material color roles. If your camera screen looks different after upgrading, see Migrating to Paper Receipt SDK 2.3.1.


Table of Contents​

  1. How It Works
  2. Default Camera Chrome
  3. Setup (Host App)
  4. The BlinkRecognizerStyle Model
  5. Theme Attribute Reference
  6. Full Example
  7. Tips and Pitfalls
  8. Migrating to Paper Receipt SDK 2.3.1

1. How It Works​

Your style feeds the camera through two independent paths: a handful of theme-level color attributes, which may reference ?attr/colorPrimary, and a set of nested component styles, each resolved on its own.

StageWhat happens
Your styleYou add a style to res/values/styles.xml that uses BlinkRecognizerStyle as its parent and overrides the attributes you want to brand.
CameraCharacteristicsYou pass the style's resource ID via CameraCharacteristics.Builder().style(...). If you omit it, the SDK uses its default style (R.style.BlinkRecognizerStyle).
SDK themingThe SDK applies your style to the camera screen and wraps the Compose context in it.
Attribute resolutionThe SDK resolves each theme attribute and builds the internal BlinkRecognizerStyle model that the camera UI renders from. The theme-level color attributes may point at ?attr/colorPrimary; the nested component styles are resolved on their own and do not inherit from your Material color roles.
Unset attributesAnything you don't declare falls back to the SDK's own default — never to a blank or transparent value.

2. Default Camera Chrome​

Out of the box, the camera ships with a fixed, high-contrast neutral palette chosen to stay legible over arbitrary camera imagery: a dark scrim behind the overlay controls (torch, hint, cancel, retake), a white capture and confirm button, and a cobalt finish button.

Role in the camera chromeValue
Overlay control container#262626
Overlay control pressed state#3D3D3D
Finish (primary action) button#0062F2
Capture / confirm button#FFFFFF
Tooltip background#262626

Brand colors are applied per component rather than inherited from your app's Material color roles. That keeps the camera chrome readable whatever your brand palette is, and it means you can brand one button without disturbing the others. Step 3 shows the pattern.


3. 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_pressed">#0846BF</color>
<color name="brand_on_primary">#FFFFFF</color>
<color name="brand_chrome">#262626</color>
<color name="brand_chrome_pressed">#3D3D3D</color>
<color name="brand_chrome_icon">#FFFFFF</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">
<!-- Drives the on-screen guidance hint and the scan progress bar -->
<item name="colorPrimary">@color/brand_primary</item>
<item name="colorOnPrimary">@color/brand_on_primary</item>

<item name="guidelineHintViewBackground">?attr/colorPrimary</item>
<item name="progressBarColor">?attr/colorPrimary</item>
</style>
tip

colorPrimary covers the on-screen guidance hint and the scan progress bar. The buttons and the tooltip are branded separately, in Step 3 — brand only the ones you care about and the rest keep the SDK defaults.

Step 3 — Brand the buttons and tooltip​

Each button and the tooltip has its own nested style. Point the corresponding attribute at your own style, then override just the colors and icons you want:

<style name="BlinkRecognizerStyle.YourBrand">
<!-- ...theme-level items from Step 2... -->
<item name="finishButtonStyle">@style/Widget.AppCompat.ImageButton.FinishButtonStyle.YourBrand</item>
</style>

<style name="Widget.AppCompat.ImageButton.FinishButtonStyle.YourBrand">
<item name="android:colorActivatedHighlight">@color/brand_primary</item>
<item name="android:colorAccent">@color/brand_primary</item>
<item name="android:colorPressedHighlight">@color/brand_primary_pressed</item>
<item name="android:tint">@color/brand_on_primary</item>
</style>
note

Nested styles use dot-notation inheritance too. Widget.AppCompat.ImageButton.FinishButtonStyle.YourBrand inherits from the SDK's Widget.AppCompat.ImageButton.FinishButtonStyle, so any attribute you leave out keeps the SDK's default icon and color rather than rendering blank.

See §5 Theme Attribute Reference for every attribute each component accepts, and §6 Full Example for a theme that brands all of them.

Step 4 — 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()
)
note

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.


4. 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 propertyControlsThemed by
guidelineHintColorBackground of the on-screen guidance hint shown over the camera.guidelineHintViewBackground
shutterSurfaceColorSurface that animates like a camera shutter on capture.shutterSurfaceColor
progressBarColorProgress bar shown while a scan is being processed.progressBarColor
tooltipStyleInformational tooltips (e.g. "The image is a bit blurry…").blinkTooltipStyle
torchButtonStyleFlashlight / torch toggle button.torchButtonStyle
cancelButtonStyleCancel (exit) button.cancelButtonStyle
hintButtonStyleHint button that opens the on-screen scanning guidance.hintButtonStyle
retakeButtonStyleRetake-capture button.retakeButtonStyle
finishButtonStyleFinish / done button.finishButtonStyle
captureButtonStyleMain camera capture (shutter) button.captureButtonStyle
confirmButtonStyleConfirm / "add another photo" button.confirmButtonStyle

5. 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 as of Paper Receipt SDK 2.3.1.

The attribute names in these tables are the supported theming surface. The SDK's own drawables, colors, and dimensions are internal and can change between releases — always point these attributes at resources in your own app.

Top-level colors​

Declared directly on your style:

AttributeFormatControlsDefault
colorPrimarycolorPrimary brand color. Used as the default value for guidelineHintViewBackground and progressBarColor.#0062F2
colorPrimaryContainercolorMaterial 3 container color read by the Compose layer. Replaces colorPrimaryVariant, which is no longer read.White
colorOnPrimarycolorForeground color on primary surfaces, read by the Compose layer. The tooltip and buttons set their own text and icon colors.White
guidelineHintViewBackgroundcolorBackground of the on-screen guidance hint.?attr/colorPrimary
progressBarColorcolorColor of the scan progress bar.?attr/colorPrimary
shutterSurfaceColorcolorColor of the capture shutter animation surface.Black

Tooltip — blinkTooltipStyle​

blinkTooltipStyle references a child style with these attributes:

AttributeFormatControlsDefault
tooltipColorcolorTooltip background and pointer color.#262626
tooltipTextColorcolorTooltip text color.#FFFFFF
tooltipPointHeightdimensionHeight of the tooltip's triangular pointer.8dp
tooltipBoxRadiusdimensionCorner radius of the tooltip body.16dp

Which tooltips appear, and for how long, is configured separately through TooltipCharacteristics — see Configuring tooltips. Their copy comes from string resources — see Camera UI Customization.

Torch button — torchButtonStyle​

AttributeFormatControlsDefault
android:colorAccentcolorButton container (background) color.#262626
android:colorPressedHighlightcolorRipple color on press.#3D3D3D
android:paddingdimensionPadding around the icon.0dp
torchOnSrcreferenceDrawable for the torch-on (activated) state.ic_torch_on
torchOffSrcreferenceDrawable for the torch-off (normal) state.ic_torch
torchOnContentDescriptionstringAccessibility text for the torch-on state.Torch On
torchOffContentDescriptionstringAccessibility text for the torch-off state.Torch Off
android:tintcolorIcon tint.White

The default ic_torch and ic_torch_on artwork was redrawn in 2.3.1. If you set torchOnSrc / torchOffSrc to your own drawables, nothing changes for you.

Hint button — hintButtonStyle​

New in 2.3.1. The hint button sits alongside the torch button and opens the on-screen scanning guidance.

AttributeFormatControlsDefault
android:colorAccentcolorButton container (background) color.#262626
android:colorPressedHighlightcolorRipple color on press.#3D3D3D
android:paddingdimensionPadding around the icon.0dp
android:srcreferenceButton icon.ic_hint
android:contentDescriptionstringAccessibility text.Hint
android:tintcolorIcon tint.White

Cancel button — cancelButtonStyle​

AttributeFormatControlsDefault
android:colorAccentcolorButton container (background) color.#262626
android:colorPressedHighlightcolorRipple color on press.#3D3D3D
android:paddingdimensionPadding around the icon.12dp
android:srcreferenceButton icon.ic_exit
android:contentDescriptionstringAccessibility text.@null
android:tintcolorIcon tint.White

Action buttons — retakeButtonStyle, finishButtonStyle, captureButtonStyle, confirmButtonStyle​

These four buttons share the same attribute shape — each renders as an inner and outer circle — but they no longer share the same defaults:

AttributeFormatRetakeFinishCaptureConfirm
android:colorActivatedHighlightcolor#3D3D3D#0062F2#FFFFFF#FFFFFF
android:colorAccentcolor#262626#0062F2#FFFFFF#FFFFFF
android:colorPressedHighlightcolor#3D3D3D#0062F2#FFFFFF#FFFFFF
android:srcreferenceic_retakeic_confirmic_camera_captureic_add
android:paddingdimension0dp0dp0dp0dp
android:tintcolorWhiteWhiteBlackBlack
android:contentDescriptionstring@null@null@null@null

android:colorActivatedHighlight is the primary (outer circle) container color, android:colorAccent the secondary (inner circle) container color, and android:colorPressedHighlight the ripple color on press.

Attributes the SDK does not read

These attributes are accepted by the resource compiler but have no effect on the camera UI. Set the listed replacement instead.

AttributeWhereUse insteadIgnored since
android:backgroundAny button styleandroid:colorAccent, android:colorPressedHighlight, android:colorActivatedHighlight1.9.10
android:srcTorch button onlytorchOnSrc and torchOffSrc1.9.10
android:statusBarColor, android:navigationBarColor, android:enforceStatusBarContrast, android:enforceNavigationBarContrastTheme levelNothing — the camera manages its own system bars1.9.10
colorPrimaryVariantTheme levelcolorPrimaryContainer2.3.1
colorSecondary, colorSecondaryVariantTheme levelThe per-button android:colorAccent / android:colorPressedHighlight2.3.1

android:src is read on the hint, cancel, retake, finish, capture, and confirm buttons — the exclusion applies only to the torch, which is a two-state control.

Note the android: prefix on android:tint. The unprefixed tint attribute is a different resource and is not read.


6. Full Example​

A complete theme that brands every customizable surface. Copy it into your res/values/styles.xml, rename it to your brand, and point the drawable, color, and string references at resources in your own app.

<resources>

<style name="BlinkRecognizerStyle.YourBrand">
<!-- Theme-level colors -->
<item name="colorPrimary">@color/brand_primary</item>
<item name="colorOnPrimary">@color/brand_on_primary</item>
<item name="guidelineHintViewBackground">?attr/colorPrimary</item>
<item name="progressBarColor">?attr/colorPrimary</item>
<item name="shutterSurfaceColor">@android:color/black</item>

<!-- Per-component styles -->
<item name="blinkTooltipStyle">@style/Tooltip.YourBrand</item>
<item name="torchButtonStyle">@style/Widget.AppCompat.ImageButton.TorchButtonStyle.YourBrand</item>
<item name="hintButtonStyle">@style/Widget.AppCompat.ImageButton.HintButtonStyle.YourBrand</item>
<item name="cancelButtonStyle">@style/Widget.AppCompat.ImageButton.CancelButtonStyle.YourBrand</item>
<item name="retakeButtonStyle">@style/Widget.AppCompat.ImageButton.RetakeButtonStyle.YourBrand</item>
<item name="finishButtonStyle">@style/Widget.AppCompat.ImageButton.FinishButtonStyle.YourBrand</item>
<item name="captureButtonStyle">@style/Widget.AppCompat.ImageButton.CaptureButtonStyle.YourBrand</item>
<item name="confirmButtonStyle">@style/Widget.AppCompat.ImageButton.ConfirmButtonStyle.YourBrand</item>
</style>

<style name="Tooltip.YourBrand">
<item name="tooltipPointHeight">8dp</item>
<item name="tooltipBoxRadius">16dp</item>
<item name="tooltipColor">@color/brand_chrome</item>
<item name="tooltipTextColor">@color/brand_chrome_icon</item>
</style>

<!-- Overlay controls: keep high contrast against the live camera preview -->
<style name="Widget.AppCompat.ImageButton.TorchButtonStyle.YourBrand">
<item name="android:colorAccent">@color/brand_chrome</item>
<item name="android:colorPressedHighlight">@color/brand_chrome_pressed</item>
<item name="android:padding">0dp</item>
<item name="torchOnSrc">@drawable/your_ic_torch_on</item>
<item name="torchOffSrc">@drawable/your_ic_torch_off</item>
<item name="torchOnContentDescription">@string/your_cd_torch_on</item>
<item name="torchOffContentDescription">@string/your_cd_torch_off</item>
<item name="android:tint">@color/brand_chrome_icon</item>
</style>

<style name="Widget.AppCompat.ImageButton.HintButtonStyle.YourBrand">
<item name="android:colorAccent">@color/brand_chrome</item>
<item name="android:colorPressedHighlight">@color/brand_chrome_pressed</item>
<item name="android:padding">0dp</item>
<item name="android:src">@drawable/your_ic_hint</item>
<item name="android:contentDescription">@string/your_cd_hint</item>
<item name="android:tint">@color/brand_chrome_icon</item>
</style>

<style name="Widget.AppCompat.ImageButton.CancelButtonStyle.YourBrand">
<item name="android:colorAccent">@color/brand_chrome</item>
<item name="android:colorPressedHighlight">@color/brand_chrome_pressed</item>
<item name="android:padding">12dp</item>
<item name="android:src">@drawable/your_ic_close</item>
<item name="android:contentDescription">@string/your_cd_close</item>
<item name="android:tint">@color/brand_chrome_icon</item>
</style>

<!-- Review controls -->
<style name="Widget.AppCompat.ImageButton.RetakeButtonStyle.YourBrand">
<item name="android:colorActivatedHighlight">@color/brand_chrome_pressed</item>
<item name="android:colorAccent">@color/brand_chrome</item>
<item name="android:colorPressedHighlight">@color/brand_chrome_pressed</item>
<item name="android:src">@drawable/your_ic_retake</item>
<item name="android:tint">@color/brand_chrome_icon</item>
</style>

<style name="Widget.AppCompat.ImageButton.FinishButtonStyle.YourBrand">
<item name="android:colorActivatedHighlight">@color/brand_primary</item>
<item name="android:colorAccent">@color/brand_primary</item>
<item name="android:colorPressedHighlight">@color/brand_primary_pressed</item>
<item name="android:src">@drawable/your_ic_done</item>
<item name="android:tint">@color/brand_on_primary</item>
</style>

<!-- Capture controls: high-luminance surface, dark icon -->
<style name="Widget.AppCompat.ImageButton.CaptureButtonStyle.YourBrand">
<item name="android:colorActivatedHighlight">@color/brand_capture_surface</item>
<item name="android:colorAccent">@color/brand_capture_surface</item>
<item name="android:colorPressedHighlight">@color/brand_capture_surface</item>
<item name="android:src">@drawable/your_ic_capture</item>
<item name="android:tint">@color/brand_on_capture_surface</item>
</style>

<style name="Widget.AppCompat.ImageButton.ConfirmButtonStyle.YourBrand">
<item name="android:colorActivatedHighlight">@color/brand_capture_surface</item>
<item name="android:colorAccent">@color/brand_capture_surface</item>
<item name="android:colorPressedHighlight">@color/brand_capture_surface</item>
<item name="android:src">@drawable/your_ic_add</item>
<item name="android:tint">@color/brand_on_capture_surface</item>
</style>
</resources>
note

Every @drawable/your_*, @color/brand_*, and @string/your_* reference above lives in your app. The SDK's own drawables and colors are internal and are not guaranteed to survive an upgrade — don't reference them from your theme.

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 and colors you override.

Launch the camera with this theme:

CameraCharacteristics.Builder()
.style(R.style.BlinkRecognizerStyle_YourBrand)
.build()

7. Tips and Pitfalls​

Extend the base style. Always use BlinkRecognizerStyle (or one of its Widget.AppCompat.ImageButton.* button styles, including HintButtonStyle, or 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.

Brand what users notice first. colorPrimary covers the guidance hint and the progress bar. Buttons and the tooltip each have their own style — start with the capture and finish buttons, which carry the most brand weight, and add the rest as needed.

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.

Some attributes are silently ignored. android:background, android:src on the torch button, colorPrimaryVariant, colorSecondary, and colorSecondaryVariant compile fine but have no effect. Use the replacements in the warning above. Note the android: prefix on android:tint — the unprefixed tint attribute is not read.

Test over a live preview. The camera chrome sits on top of an unpredictable image. Check your colors against both a bright receipt and a dark background before shipping.


8. Migrating to Paper Receipt SDK 2.3.1​

Themes written against 2.3.0 and earlier still compile and still apply. What follows is organized by what you'd actually observe after upgrading.

8.1 Your build fails​

This only happens if your theme references an SDK-internal resource that was removed in 2.3.1.

Resources removed from the SDK

If your styles.xml references any of these, replace them with equivalents in your own app:

Drawables — default_secondary_button_background, ic_torch_selector, primary_secondary_color_selector, tooltip_secondary_action_button_selector

Colors — see_through_white, blink_blue_tapped, chip_background

Dimensions — secondary_btn_diameter, primary_btn_diameter

8.2 Your build succeeds but your colors are ignored​

Before (2.3.0 and earlier)After (2.3.1 onwards)
colorPrimaryVariantcolorPrimaryContainer
colorSecondary, colorSecondaryVariantPer-button android:colorAccent / android:colorPressedHighlight
android:background on a button styleandroid:colorAccent, android:colorPressedHighlight, android:colorActivatedHighlight
Unprefixed tintandroid:tint

The full list is in Attributes the SDK does not read.

8.3 Your colors apply but the camera looks different​

Expected — this is the refreshed default chrome.

Before 2.3.1, the buttons and tooltip inherited from colorPrimary / colorSecondary. They now carry their own defaults, so a theme that only set the Material color roles will show the SDK's default chrome. Add the per-component styles from §6 Full Example to restore your branding, and see §2 Default Camera Chrome for the new palette.

8.4 New in 2.3.1​

  • Hint button — a new themeable overlay control. See hintButtonStyle.
  • TooltipCharacteristics.displayDataMissingTooltip — gates the "No receipt detected" tooltip, which was previously unreachable. See Configuring tooltips.
  • String resource changes — several camera strings were removed and others reworded. See Camera UI Customization.