Recognizer SDK (2.5.1)
- Requires activation SDK 1.3.0. Hosts using activations call
ActivationClient.initialize(privacy)inApplication.onCreate(), in either order withBlinkReceiptSdk.initialize(); until they do, activation features stay off and one error is logged instead of the SDK crashing. - Frames captured after the user has taken a photo no longer reach the OCR parser. Because the parser accumulates text across the frames of a session and resolves each line item by cross-frame vote, motion-blurred frames picked up while the user reviewed a capture could outvote the capture itself and corrupt line-item names.
- Added
RecognizerView.holdFrameAnalysisAfterCapture(boolean), off by default.takePicture()pauses analysis frames at the shutter and resumes them once the photo is delivered, so existing integrations need no change. A host that shows the captured photo for review can turn this on to keep frames paused untilresumeFrameAnalysis(), protecting line-item text from frames picked up during review. The bundled camera UI does this. - A capture that fails, including one the camera delivers but that cannot then be processed, is now reported through
CameraRecognizerCallback.onException()instead of being dropped silently. - A captured photo is no longer discarded by the frame queue when a sharper preview frame is already held, and a queued capture is no longer displaced by a later preview frame.
- Camera frames are no longer handed out while still referencing the buffer the next frame is converted into, which could overwrite a frame already queued for OCR and halves per-frame bitmap allocation at the same time.
- Merchant-detection signals are now passed to the Activation SDK through
ScanResults.merchantDetection. - With remote OCR, the inverted-text retry now uploads the full-resolution frame instead of a downscaled one.
- Stability fixes and improvements
Account Linking SDK (2.5.1)
- Stability fixes and improvements
Camera SDK (2.5.1)
CameraView.captureImage()now suspends analysis-frame delivery at the shutter.onImageCaptured()is unaffected and the camera stays bound.RecognizerViewresumes delivery once the photo is delivered, so integrations using it need no change; a host that subclassesCameraViewitself resumes withresumeFrameAnalysis(). A capture that fails resumes delivery and is reported throughonError(); a subclass that receivesonImageCaptured()but cannot use the frame should callonCaptureFailed()so the same happens.- Added
CameraView.suspendFrameAnalysis(),CameraView.resumeFrameAnalysis(), and the protectedCameraView.isFrameAnalysisSuspended()andCameraView.onCaptureFailed(). CameraView.captureImage()no longer throws when called before the camera has bound; it reports the failure throughonError()instead.- Stability fixes and improvements
Camera UI SDK (2.5.1)
- Line-item names are no longer garbled by frames picked up while the user reviews a captured photo; the camera UI now keeps frame analysis paused during review.
- Activation scans now start their scan session once activation becomes ready, even after the camera has opened, and a scan left without a session ends with an error. Adds the overridable
activation_scan_session_missingstring and rewordsactivation_sdk_not_initialized. - Stability fixes and improvements
Core SDK (2.5.1)
- Stability fixes and improvements
Digital SDK (2.5.1)
- Stability fixes and improvements
Digital Analytics SDK (2.5.1)
- Stability fixes and improvements
Earnings SDK (2.5.1)
- Stability fixes and improvements
Logcat SDK (2.5.1)
- Stability fixes and improvements
Security SDK (2.5.1)
- Stability fixes and improvements
Surveys SDK (2.5.1)
- Stability fixes and improvements
Activation SDK 1.3.1 for Android
These notes cover both 1.3.0 (released October 29, 2026) and 1.3.1 (released November 1, 2026), for
Android apps upgrading from 1.2.0. 1.3.1 changes one thing on top of 1.3.0: the privacy type you
pass when starting the SDK is named Privacy instead of BlinkPrivacy. Everything below already
uses the 1.3.1 name.
This update adds privacy-aware ad consent, lets you render the curated offer feed with your own UI,
introduces merchant promos that reward total spend at a store rather than specific products, and
lets the backend override the scan reward per store. It also fixes a round of reward-accuracy,
session, and registration issues, including the 1.2.0 known issue where review requests failed with
"Scan session not found".
This release contains breaking changes. Read the Upgrade Notes before bumping.
dependencies {
implementation(platform("com.actualplatform:activation-bom:1.3.1"))
implementation("com.actualplatform:activation")
}Upgrade Notes
-
ActivationClient.initialize(privacy)is now the only way to start the SDK. The no-argument
initialize()is gone, andActivationClient.instanceno longer creates the client on first
access, so reading it before initializing throws. Callinitialize(privacy)in
Application.onCreate(), before you start any dependency-injection graph whose singletons reach
for the SDK. Calling it again on a started SDK replaces the privacy values instead of doing
nothing.class MyApplication : Application() { override fun onCreate() { super.onCreate() ActivationClient.initialize( Privacy( sellOrShareStatus = ConsentState.UNKNOWN, personalizationStatus = ConsentState.UNKNOWN, disclosureVersion = null, rdpOwner = RdpOwner.ACTUAL, ) ) } }
-
If your app already has its own
Privacytype, the two names will clash. Disambiguate the
SDK's type with an import alias, such as
import com.actualplatform.activation.Privacy as ActivationPrivacy. -
Supply the user's privacy choices on every cold start. The privacy values you pass are held
in memory only and never written to disk, so supply them again after every cold start, reinstall,
and account switch. Every field is required. A value you have not resolved isUNKNOWN, and
UNKNOWNrestricts, so you may see fewer ads, or non-personalized ones, until real values arrive.
If you setrdpOwnertoRdpOwner.PUBLISHER, the SDK never writes Google'sgad_rdp
preference. When restricted processing is required and you have not turned it on, no ad is
requested at all. -
Register the new Ad Manager targeting keys. Every ad request now carries the applied consent
decision as custom targeting:npa,rdp,adid_state,sdk_version, andapp_version. These
keys must be registered in Ad Manager with predefined values before they show up in your reports. -
The Google ad events are renamed.
ad_requestreplacesGAM_AD_REQUESTED, andad_response
replaces bothGAM_AD_LOADEDandGAM_AD_FAILED. The oldadType,loadedAdType, anderror
fields are gone. Both events now carry the consent decision that governed the request.
ad_requestfires only once a request actually goes out, so loads refused by consent and
rewarded ads served from cache no longer count as requests. Dashboards keyed on the old names
will see no new rows from 1.3.0 onward. -
Reward amounts change in several cases. If you reconcile rewards on your backend, expect
these differences:- No merchant, no reward. A scan whose merchant can't be identified now earns nothing. The
Receipt Summary shows the merchant as---and a zero total, and withholds the base scan
reward, qualified promotions, ad boosts, and banked boost credit. The scanned line items still
appear. No error is shown, because nothing is wrong with the receipt. - Promotions are paid once. A promotion that covers several scanned products is now paid
once, at the amount the promotion actually awarded. Previously it was paid once per product. - Overlapping promotions resolve differently. When two promotions qualify the same product,
the last one listed now claims it. - The scan reward can be overridden per store. The backend can replace your configured
ScanRewardfor a specific store, including with zero. The Receipt Summary and
Rewards.ScanFinishedalways carry the resolved amount, so credit the shopper from those
rather than from your own configured value.
- No merchant, no reward. A scan whose merchant can't be identified now earns nothing. The
-
OfferandActivationAppearance.OffersWall.Colorsgained parameters in the middle of their
constructors.Offergained ten fields mid-signature, andOffersWall.Colorsgained
offerRedemptionLabel. EveryOffersWall.Colorsparameter is aLong?, so positional
construction still compiles, but each later color shifts onto the wrong key. Use named arguments
for both. -
CouponGoal.spendis removed. It read a key the backend never sends, so it was always null.
Use the newCouponGoal.minimumSpendfor a spend-based goal's threshold.CouponGoal.quantity
reads0for spend-based goals. -
GoogleSdkInstructionsgained a requiredrequiresRestrictedMode. This only affects code
that constructs the model itself. -
Recompile against 1.3.1. Every other model and composable change (
AdUnit,AdImage,
Activations,Placement,ScanResults,OffersWall,OffersWallFlow) adds defaulted
parameters at the end. Those changes are source-compatible, but as with every release, your
integration must be recompiled against the new version.
The compile target is unchanged from 1.2.0: compileSdk 37 or higher is still required, your app's
targetSdk does not need to change, and no new dependencies are introduced. The matching Receipts
SDK release for 1.3.0 is 2.5.1.
What's New & Improved
-
Ad consent. The SDK now serves personalized ads where the law allows and restricts itself
where it doesn't. You report the user's privacy choices throughinitialize(privacy), and can
update them later throughActivationClient.privacy. ThePrivacyvalues carry:sellOrShareStatusandpersonalizationStatus, eachOPTED_OUT,NOT_OPTED_OUT, or
UNKNOWNdisclosureVersion, the version of the privacy notice the user sawrdpOwner, which names who may write Google'sgad_rdppreference
Before every ad request, the SDK combines these values with the backend's judgement of the
privacy notice. It then makes one of three requests: personalized, non-personalized, or none at
all. A personalized request is never sent while restricted processing is in effect. If the
user's privacy choices change after a rewarded ad has preloaded, that ad is dropped rather than
shown. Each decision is reported as anad_consent_decisionevent, and each refusal as
ad_suppressed. Both carry the inputs and the applied values, so any ad request can be
reconstructed from its events. The advertising identifier itself is never used, logged, or
transmitted. -
Render the offer feed with your own UI.
ActivationClient.api.refreshOfferFeed()returns the
backend-curated feed asOfferFeed,OfferGroup, andOfferSummary, in the order the backend
sends. To follow the feed as it changes, observeActivationClient.api.offerFeed. It isnull
until the feed first loads, and an empty feed is distinct from that. A failed refresh returns an
OfferFeedErrorcategory.DISABLEDmeans the feed is switched off for your client, while the
offer wall keeps working.OfferSummaryis sealed intoProductCouponandMerchantPromo, so
keep anelsebranch when matching on it. When a shopper taps one of your cards, pass the new
initialDestinationtoOffersWallorOffersWallFlow:OffersWallDestination.OfferDetailsopens that offer's detail sheet.OffersWallDestination.Groupopens the wall at that group.
If the offer is no longer available, the wall opens at its root with a short notice. Clipping an
offer inside the SDK updatesofferFeedright away, without another fetch. -
Merchant promos. A new offer type rewards total spend at a store instead of specific
products, and it sits alongside the existing product offers. UseOffer.couponType,
Offer.isMerchantPromo, andOffer.minimumSpendThresholdto tell these offers apart. On the
offer wall:- Merchant promos show a "SPEND $X" tag in place of "BUY N", rounded up so the threshold is never
understated. - A dashed border sets them apart from standard offers.
- An "Nd left" label marks offers with a redemption period. It is themed with
ActivationAppearance.OffersWall.Colors.offerRedemptionLabel, which defaults to your theme's
warning color.
A qualifying scan earns the reward immediately and shows its own "Merchant Boost" card in the
Receipt Summary's Your Earnings section. - Merchant promos show a "SPEND $X" tag in place of "BUY N", rounded up so the threshold is never
-
Offer eligibility by store. New
Offer.redemptionLimit,Offer.currency, and
Offer.excludedFromMerchantsdescribe how often an offer can be redeemed and where it can't be
used.Offer.appliesToBanner(bannerId)combines the existing store restrictions and the new
exclusions into one check. The offer wall's per-store lists now leave out offers excluded from
that store. -
Per-store scan rewards. The base scan report now includes the detected store, and the
backend can answer with a reward for that store that replaces your configuredScanReward. The
report is sent even when you have configured noScanReward, so the backend can set the reward
entirely on its own. If the backend doesn't supply an override in time, your configured amount
stands. -
Offer image placeholders. When a product or store image is missing or fails to load, offer
cards, list items, and the detail screen now show a placeholder instead of an empty space. -
Better merchant attribution. You can now attach the scanner's merchant-detection signals to a
scan through the optionalScanResults.merchantDetection. They are forwarded with the scan, so a
mis-detected merchant can be traced to the detector responsible. Leave it unset and nothing new
is sent. -
One base scan event per receipt. The base scan reward is now reported as soon as processing
starts. This includes receipts whose verdict later withholds rewards, such as duplicate, invalid,
too old, or over the submission limit. Each receipt produces exactly one base scan event. -
More offer metadata.
AdImagenow carries the backend'saspectRatio,backgroundFill, and
fitfor normalized artwork, andPlacementcarries its numericid.
Bug Fixes
- Fixed review requests for missing fields, an unreadable receipt, or a too-old receipt failing
with "Scan session not found". This was the Known Issue in 1.2.0. Every new scan session is now
announced to the backend before a review can reference it. - Fixed a
401 Invalid API tokenending the shopper's session at the first hiccup.
Re-registration is now retried up to three times with increasing back-off, and the original
request is replayed once a new token arrives. A replayed request now also carries its body
correctly, so a recoverable 401 no longer turns into a hard failure. - Fixed an identity change (
hashedEmail,hashedPhone,clientUserId,deviceId,
appBundleId, orenvironment) leaving the previous user's state in play. The SDK now clears
the cached offers immediately and cancels requests still in flight. Every request goes out under
the identity that was current when it was made, soofferFeedand the offer wall can never show
one user's offers to the next. Work started under the old identity is dropped instead of being
sent with the new user's token. - Fixed leftover work from a closed
ActivationClientaffecting the next one. Calls on a closed
client now do nothing. - Fixed a promotion and its product showing different reward values on the Offer Wall and Offer
Detail than on the Receipt Summary. All three now show the full amount. - Fixed an offer wall response that contained a spend-based merchant promo failing to load as a
whole, which left the offer wall blank. - Fixed a failed offer wall reload blanking the catalog already on screen. The wall now keeps what
it shows. - Fixed the offer wall shimmering over, and blocking taps on, content it was already showing while
a reload was in flight. - Fixed the system back gesture at the offer wall's root bypassing
onDismissand closing your
host screen. Back at the root now reports throughonDismiss, the same as the header's Up
button. If you leaveonDismissnull, nothing changes and your app keeps owning back. - Fixed reopening the offer wall bringing back a detail sheet or stores sheet left open from the
previous visit. - Fixed filling in a missing date or total reporting the base scan reward twice for the same
receipt. - Fixed an accepted review's base scan report being cancelled when the scan flow was closed while
it was still sending. - Fixed the loading screen reporting failed ads that did not fail. A no-fill was reported twice,
and a successful ad was reported again as failed when the receipt summary opened. - Fixed Test Mode not registering the device as a Google Mobile Ads test device. Ad requests are
now correctly flagged as test requests. - Fixed the date picker cutting off its right-most weekday column, which left those dates
untappable. This affected the Receipt Date dialog in Missed Earnings and the missing-date and
too-old review forms.