Skip to main content

Android Appearance Customization

Activation.theme controls how the SDK looks SDK-wide; Activation.appearance layers per-screen overrides on top. Where the theme is a flat catalogue of ~20 semantic color roles that cascade everywhere, the appearance is organised per screen, and each screen exposes two kinds of override:

  • Labels — the wording shown on a screen (e.g. the scan-receipt FAB text). String?; null → the SDK's bundled, localized default.
  • Colors — fine-grained, per-element color overrides (e.g. just the offer-card background, or just the receipt-summary header band). Long? (0xAARRGGBB); null → the exact ActivationTheme token that element already renders with.

Theme first, appearance second: set your brand roles on Activation.theme and the whole SDK follows. Reach for appearance colors only when you need to override one specific element beyond what the shared roles give you — a host that sets nothing sees pixel-identical rendering to the un-customised SDK.

Activation.appearance = ActivationAppearance(
offersWall = ActivationAppearance.OffersWall(
colors = ActivationAppearance.OffersWall.Colors(
offerWallBackground = 0xFFFFFFFF, // override just this screen's background
),
labels = ActivationAppearance.OffersWall.Labels(
scanExtendedLabel = "Scan your receipt",
),
),
)

Like the theme, Activation.appearance is settable from any thread, any time, with no Compose import — and updates propagate live to every SDK screen currently composed.


Structure

The appearance is a nested tree: one member per screen. Every screen carries a Colors record; OffersWall additionally carries a Labels record. Every field defaults, so you override only what you need:

data class ActivationAppearance(
val offersWall: OffersWall = OffersWall(),
val loading: Loading = Loading(),
val errorModal: ErrorModal = ErrorModal(),
val receiptSummary: ReceiptSummary = ReceiptSummary(),
val missedEarnings: MissedEarnings = MissedEarnings(),
) {
data class OffersWall(val colors: Colors = Colors(), val labels: Labels = Labels()) {
data class Labels(val scanLabel: String? = null, val scanExtendedLabel: String? = null)
data class Colors(/* per-element Long? overrides — see tables below */)
}
data class Loading(val colors: Colors = Colors()) { data class Colors(/* … */) }
data class ErrorModal(val colors: Colors = Colors()) { data class Colors(/* … */) }
data class ReceiptSummary(val colors: Colors = Colors()) { data class Colors(/* … */) }
data class MissedEarnings(val colors: Colors = Colors()) { data class Colors(/* … */) }
}

Labels

Labels for the offers-wall scan-receipt FAB. Each field is a String? defaulting to null, meaning "use the SDK's built-in localized default" — set one only to override the bundled string.

offersWall.labels.scanLabel

Type: String? · Default: null

Label on the scan-receipt FAB in its normal (collapsed) state. null falls back to the SDK's bundled, localized default.

offersWall.labels.scanExtendedLabel

Type: String? · Default: null

Label on the scan-receipt FAB in its extended (expanded) state. null falls back to the SDK's bundled, localized default.

Localization

The bundled defaults are localized by the SDK. A value you set here is shown verbatim in every locale — supply your own localized string if you override it.


Colors — The Override Model

Every appearance color is a Long? in 0xAARRGGBB form (the same encoding as theme colors):

  • null (default) → the element renders with the exact ActivationTheme.Colors token it uses today. The fallback is baked in per key (see the tables below) — it is not always the same token, it's whatever that specific element currently reads.
  • A value → applied as-is. You control alpha through the 0xAA byte.

This makes the override model non-destructive by construction: the fallback for every key reproduces current rendering — including alpha-composed treatments like the loading progress track (textInverse at 16% alpha) or the edited-row highlight (success at 10% alpha) — so leaving a key unset can never change a pixel.

Because the fallback is a theme token, appearance colors and theme roles compose cleanly: override primary on the theme and every element keyed to it (including appearance-color fallbacks) follows; then set one specific element's appearance color to peel it off that role.

Colors vs. theme roles — which do I set?

GoalSet
Rebrand the whole SDKActivation.theme color roles
Change one element on one screenActivation.appearance.colors

Colors by Screen

Each table lists Field · Fallback (the theme role used when null) · Element. All fields are Long?, default null.

offersWall.colors

Covers the Offer Wall list, Offer Card (grid + list variants), Offer Details, the Clip/Clipped state, and the Stores sheet.

Offer Wall

FieldFallbackElement
offerWallBackgroundbackgroundScreen background (Scaffold + top-app-bar)
offerWallSectionHeaderLabeltextPrimarySection header title
offerWallSectionHeaderShowMoreIconaccentSection "show more" icon (built-in fallback painter)
offerWallSectionHeaderShowMoreBackgroundsurfaceAccentSection "show more" button container
offerWallFloatingButtonBackgroundprimaryScan-receipt FAB container
offerWallFloatingButtonLabeltextOnPrimaryScan-receipt FAB label + icon tint
offerWallMoreMerchantsIcontint-only"More merchants" chip icon — see Special cases

Offer Card (grid + list)

FieldFallbackElement
offerBackgroundsurfaceCard container
offerBrandLabeltextPrimaryBrand / product title
offerEligibleMerchantsLabeltextAccent"Eligible at …" line
offerRewardPointsLabeltextAccentReward-points amount
offerTagLabeltagText"Buy N" tag text
offerTagBackgroundtagSurface"Buy N" tag pill background

Clip / Clipped state

FieldFallbackElement
offerClipButtonIconprimaryUnclipped clip-action icon (card + details)
offerClipButtonBackgroundsurfaceUnclipped clip-action container
offerClippedButtonIconsuccessClipped-state icon
offerClippedButtonBackgroundsurfaceClipped-state container
offerClippedToastMessageLabeltextPrimary"Offer clipped" toast / snackbar text
offerClippedToastMessageBackgroundsurface"Offer clipped" toast / snackbar surface

Offer Details

FieldFallbackElement
offerDetailsTitleLabeltextPrimaryTitle
offerDetailsEarnRewardLabeltextAccentEarn / short-description subtitle
offerDetailsClipLabeltextPrimary"CLIP" action label
offerDetailsSectionHeaderTitleLabeltextPrimarySection headers (How to Qualify / Eligible Products / Valid Stores / Qualifying Details)
offerDetailsSectionHeaderToggleLabeltextAccentSection see-all / read-more toggles
offerDetailsSectionBodyLabeltextPrimarySection body text
offerDetailsFinePrintLabeltextPrimaryQualifying Details full terms + bullets
offerDetailsTagChipLabeltextSecondaryEligible-products tag chip text
offerDetailsTagChipBorderborderEligible-products tag chip border
offerDetailsSectionNumberedListBadgeLabeltextAccentHow-to-Qualify step-number text
offerDetailsSectionNumberedListBadgeBackgroundsurfaceAccentHow-to-Qualify step-number circle

Stores

FieldFallbackElement
storesHeaderBackgroundbackgroundStores sheet + header background
storesHeaderTitleLabeltextPrimarySheet header title
storesListBackgroundbackgroundStores list background
storesListSectionHeaderLabeltextPrimaryList section header
storesListItemBackgroundbackgroundStore card container
storesListItemDefaultIcontextSecondaryStore card fallback icon (no logo)
storesListItemTitleLabeltextPrimaryStore name
storesListItemSubtitleLabeltextSecondaryOffer-count subtitle

loading.colors

The ad-loading sheet.

FieldFallbackElement
adLoadingLoadingBarLabeltextInverseProgress-row label
adLoadingLoadingBarBackgroundtextInverse @ 16%Progress-bar track
adLoadingLoadingBarProgressprimaryProgress-bar fill
adLoadingDefaultTitleLabeltextInverseStaged headline
adLoadingDefaultDescriptionLabeltextInverse @ 70%Staged description

errorModal.colors

The scan-error modal.

FieldFallbackElement
errorModalBackgroundbackgroundDialog surface
errorModalIconBackgroundwarningError-icon tint (built-in glyph; the SDK draws a bare glyph, no circle behind it)
errorModalTitleLabeltextPrimaryTitle
errorModalDescriptionLabeltextPrimaryMessage
errorModalBackButtonLabeltextPrimaryDismiss-button content

receiptSummary.colors

The post-scan Receipt Summary, plus the shared purchase-row text colors reused by Missed Earnings rows.

FieldFallbackElement
postScanHeaderBackgroundsurfaceBrandHeader band (+ slab gradient tail + status-bar filler)
postScanTotalPointsBackgroundsecondaryTotal-points pill background
postScanTotalPointsLabeltextOnSecondaryTotal-points pill text
postScanReceiptButtonIcontextOnSecondaryMissed-earnings entry icon (built-in fallback)
postScanReceiptButtonBackgroundsecondaryMissed-earnings entry icon circle
postScanFooterBackgroundsurfaceFooter row background
postScanFooterButtonTitletextAccent"Continue" footer-button content
postScanMerchantNameLabeltextPrimaryStore name
postScanTripInfoLabeltextPrimaryTrip info (date / time)
postScanSectionHeaderTitleLabeltextSecondaryRow-group section headers
postScanNoBoostsLabeltextPrimary"No boosts" empty-state message
postScanSuccessTitleLabeltextPrimary"Nice scan" card title
postScanSuccessDescriptionLabeltextPrimary"Nice scan" card description
postScanBoostTitleLabeltextPrimaryCPA dialog title
postScanBoostDescriptionLabeltextPrimaryCPA dialog subtitle
postScanBoostSkipButtonLabelprimaryCPA "No Thanks" content
postScanBoostClaimButtonLabeltextOnPrimaryAd-boost / CPA "Redeem" content
postScanBoostClaimButtonIconwarningAd-boost icon (built-in fallback)
postScanBoostClaimButtonBackgroundprimaryAd-boost / CPA "Redeem" container
postScanPurchasePointsLabelprimaryEarned-reward points label
postScanPurchaseBackgroundsurfaceStandard purchase-row card
postScanQualifiedPurchaseBackgroundsurfaceQualified (earned-reward) row card
postScanPurchaseInfoIconborderInline-task info-icon fallback tint
postScanInlineProductTaskBackgroundsurfaceAccentInline product-task row card
postScanInlineProductTaskScanAndEarnBackgroundprimary"Scan & earn" pill background
postScanInlineProductTaskWatchAndEarnBackgroundprimary"Watch & earn" pill background
postScanInlineProductTaskScanAndEarnLabeltextInverse"Scan & earn" pill text (+ leading icon — see Special cases)
postScanInlineProductTaskWatchAndEarnLabeltextInverse"Watch & earn" pill text (+ leading icon)
postScanInlineProductTaskPointsLabelprimaryInline-task reward-points label
purchaseRowLabelColortextPrimaryProduct name — all row types + Missed Earnings (shared)
purchaseRowMetadataLabelColortextPrimaryProduct metadata line — all row types + Missed Earnings (shared)

missedEarnings.colors

The Missed Earnings list, its edit dialogs, and the missed-earnings alert modal.

FieldFallbackElement
missedEarningsNavigationTitleLabeltextPrimaryTop-bar title
missedEarningsNavigationDescriptionLabeltextPrimaryTop-bar subtitle
missedEarningsNavigationBarTexttextPrimaryBack-icon fallback tint (in-sheet only)
missedEarningsNavigationEditButtonIconaccentEdit-mode toggle icon (built-in fallback)
missedEarningsNavigationEditButtonBackgroundsurfaceAccentEdit-mode toggle chip
missedEarningsNavigationSaveButtonIcontint-onlySave icon — see Special cases
missedEarningsNavigationSaveButtonBackgroundprimarySave chip
missedEarningsFieldEditIconaccentPer-row edit-pencil icon
missedEarningsAddNewFieldLabelprimary (text) / accent (icon)"Add missing product" row — see Special cases
missedEarningsModifiedFieldBackgroundsuccess @ 10%Edited / new-product row highlight
missedEarningsListSectionTitleLabeltextPrimaryList section title
missedEarningsTripItemLabeltextPrimaryTrip / field-row value
missedEarningsEditModalBackgroundbackground (merchant) / surface (date)Edit-dialog card — see Special cases
missedEarningsEditModalTitleLabeltextPrimaryEdit-dialog title
missedEarningsEditModalInputLabeltextPrimaryEdit-dialog field label
missedEarningsEditModalInputPlaceholderLabeltextSecondaryEdit-dialog field placeholder
missedEarningsEditModalInputValueLabeltextPrimaryEdit-dialog field value
missedEarningsEditModalCancelButtonLabelprimaryDialog "Cancel" content
missedEarningsEditModalSaveButtonLabeltextOnPrimaryDialog "Save" content
missedEarningsEditModalSaveButtonBackgroundprimaryDialog "Save" container
missedEarningsEditModalDatePickerprimaryDate-picker selected day + today's-date border
missedEarningsAlertTitleLabeltextPrimaryAlert-modal title
missedEarningsAlertMessageLabeltextPrimaryAlert-modal message

Special cases

  • Tint-only-when-setofferWallMoreMerchantsIcon and missedEarningsNavigationSaveButtonIcon back elements that are untinted today. Their null default leaves the glyph untinted (it does not fall back to a theme token); set a value to tint it. Neither ever tints a host-supplied theme.icons image — only the built-in fallback glyph.
  • One key, two fallbacks — a few keys color two elements that currently use different tokens. missedEarningsAddNewFieldLabel colors the row text (primary) and its icon (accent); missedEarningsEditModalBackground backs the merchant dialog (background) and the date dialog (surface); the inline-task pill labels also tint a leading icon (primary), distinct from the pill text (textInverse). Setting the key overrides both sites; leaving it null preserves each element's own current color.
  • Shared purchase-row keyspurchaseRowLabelColor / purchaseRowMetadataLabelColor color the product name / metadata line across all three Receipt Summary row types and the Missed Earnings product rows.

Putting It Together

Per-screen overrides layered on top of a brand theme:

// Application.onCreate — brand roles first
Activation.theme = ActivationTheme(
lightColors = ActivationColorDefaults.lightColors(
primary = 0xFF7C3AED,
textAccent = 0xFF6D28D9,
surfaceAccent = 0xFFF3E8FF,
),
)

// Per-screen overrides — layered on top of the theme
Activation.appearance = ActivationAppearance(
offersWall = ActivationAppearance.OffersWall(
colors = ActivationAppearance.OffersWall.Colors(
offerWallBackground = 0xFFFAF5FF, // just this screen's background, distinct from `background`
),
labels = ActivationAppearance.OffersWall.Labels(
scanLabel = "Scan",
scanExtendedLabel = "Scan your receipt",
),
),
receiptSummary = ActivationAppearance.ReceiptSummary(
colors = ActivationAppearance.ReceiptSummary.Colors(
postScanHeaderBackground = 0xFFFFFFFF, // white header band, while buttons stay purple
),
),
)

Result: the theme rebrands the whole SDK purple; on top of that, the offers wall gets custom scan-FAB wording and a tinted background, and the receipt-summary header band is forced white — both without disturbing the purple primary role everywhere else.


FAQ

What's the difference between Activation.theme and Activation.appearance?

theme sets ~20 semantic color roles (plus shapes, typography, icons) that cascade SDK-wide. appearance applies per-screen overrides on top — both text labels and fine-grained per-element color overrides. Rebrand with theme; reach for appearance colors when you need to change one specific element beyond what the shared roles allow. They're independent hooks; set either, both, or neither.

When should I set an appearance color instead of a theme color?

Set a theme color to move a semantic role everywhere it's used (e.g. primary recolors every primary button, FAB, and progress bar at once). Set an appearance color to override a single element on a single screen without disturbing that role elsewhere — e.g. make just the receipt-summary header band white (receiptSummary.colors.postScanHeaderBackground) while your brand color stays on buttons. Every appearance color defaults to the theme token its element already uses, so unset keys change nothing.

Why are appearance labels and colors nullable?

null means "use the default." For labels, that's the SDK's built-in localized default — a value you supply is shown verbatim in every locale, so provide your own localized string if you override. For colors, null means "use the theme token this element already renders with," so unset keys can never change a pixel.

What happens if I change Activation.appearance after the SDK is on screen?

Every SDK screen currently composed recomposes with the new values. There's no flush, no reset, no lifecycle hook to call — the same live-update behavior as Activation.theme.

Do I have to import Compose to use this?

No. Activation.appearance is a plain Kotlin object — labels are String? and colors are Long?. Set it from Application.onCreate, a ViewModel, or anywhere else.