Skip to content

2.8.1 - #452

Merged
ianrumac merged 15 commits into
mainfrom
develop
Aug 11, 2026
Merged

2.8.1#452
ianrumac merged 15 commits into
mainfrom
develop

Conversation

@ianrumac

Copy link
Copy Markdown
Collaborator

2.8.1

Fixes

  • Paywalls with translations now render in the user's language on first paint instead of briefly showing the default language.
  • Paywall analytics events (paywall_open, paywall_page_view, paywall_close, etc.) now include a presentation_id, a unique identifier minted for each paywall presentation. Adds the previously-missing close_reason, cache_key, and build_id fields to these events, matching the data already sent by the iOS SDK.

chroxify and others added 15 commits August 6, 2026 14:00
…ll events

Superwall-Android never wrote presentation_id into outgoing paywall
event payloads (paywall_page_view, paywall_open, paywall_close, etc.),
which breaks any dashboard funnel that correlates a set of page views
into one paywall session. Confirmed on live ClickHouse data: the field
is 100% empty on Android across every SDK version, vs 0% empty on iOS.

- PaywallCloseReason: add a `description` extension mirroring iOS's
  camelCase close-reason strings (systemLogic, forNextPaywall,
  webViewFailedToLoad, manualClose, none).
- PaywallInfo: add `presentationId`, and serialize it alongside the
  already-modeled-but-never-emitted close_reason/cache_key/build_id in
  eventParams().
- Paywall: add a transient `presentationId` field, threaded through
  getInfo().
- PaywallRequestManager.updatePaywall: mint a fresh UUID presentationId
  on every getPaywall() call that results in a presentation (fresh
  fetch, in-flight-task reuse, and content-cache hit), so repeat
  presentations of a cached paywall get distinct, correlatable IDs.

Trade-off: PaywallLoad.Complete/PaywallProductsLoad.* events track
before updatePaywall runs, so they won't carry presentation_id — same
existing timing gap as experiment_id/variant_id/presentation_source_type.
paywall_open/paywall_page_view/paywall_close all fire after
updatePaywall and reliably get a stable ID.
Translated paywalls rendered in the default language first, then visibly
re-rendered once the template_variables message delivered deviceLocale to
paywall.js (that message is gated on product/billing loading, so it can
take seconds). The web runtime now reads window.__SW_DEVICE_PRELOAD__ at
boot and seeds its locale from it, so inject that global before any page
JavaScript runs:

- Add DevicePreloadScript, a pure builder that serializes the payload
  with kotlinx.serialization so hostile locale strings cannot break out
  of the script, producing exactly:
  window.__SW_DEVICE_PRELOAD__ = {"deviceLocale":"en_US"};
- Install it via WebViewCompat.addDocumentStartJavaScript (androidx.webkit,
  new dependency) when the WebView supports DOCUMENT_START_SCRIPT, and
  fall back to evaluateJavascript in WebViewClient.onPageStarted on older
  WebView versions.
- The locale comes from PaywallViewState.locale, which is the same
  DeviceHelper.locale value later sent as deviceLocale in
  template_variables, so the later message is a visual no-op.
- Unit-test the builder (exact output, quote escaping, longer and
  non-ASCII locales) and add a CHANGELOG entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01775Up1AYfMgNQybxjnoDSg
…entation-id

fix: emit presentation_id, close_reason, cache_key, build_id on paywall events
The test asserted externalAccountId equals sha256-of-user-123 but never
stubbed storage.read(AppUserId), so userId fell back to the generated
anonymous alias. Stub the stored app user ID like the sibling test does.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UL13jCN87cPLKtmZnTYUrb
The document-start script path required adding androidx.webkit as a new
dependency for every SDK user. It bought little: the paywall runtime
reads window.__SW_DEVICE_PRELOAD__ when its network-fetched bundle
boots, so an evaluateJavascript from onPageStarted lands well before
that — and since the web runtime now seeds exclusively from the preload
global, a missed injection just means today's behavior (wait for
template_variables), never a wrong translation.

This matches how the SDK already injects JS (plain evaluateJavascript,
like the selection/zoom scripts), just hooked at page start rather than
template delivery, which would be too late. DevicePreloadScript and its
tests are unchanged; the script is now built lazily in the hook so it
always uses the freshest locale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01775Up1AYfMgNQybxjnoDSg
…f-o43go3

Inject device locale into paywall WebView at document start
@ianrumac
ianrumac merged commit 2f32966 into main Aug 11, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants