Skip to content

Analytics rewrite: generic provider SPI with GDPR consent and multiple backends#5266

Open
shai-almog wants to merge 17 commits into
masterfrom
analytics-spi-rewrite
Open

Analytics rewrite: generic provider SPI with GDPR consent and multiple backends#5266
shai-almog wants to merge 17 commits into
masterfrom
analytics-spi-rewrite

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Summary

Replaces the deprecated Google Analytics v1 AnalyticsService with a generic provider SPI. Apps register one or more AnalyticsProvider implementations with the Analytics facade, which fans screen / event / setUserProperty / crash calls out to all providers — but only after a configurable, opt-in-by-default consent gate.

Client (com.codename1.analytics)

  • Analytics facade + AnalyticsProvider SPI (AbstractAnalyticsProvider base), AnalyticsEvent, AnalyticsConsent, ConsentMode, AnalyticsContext, AnalyticsCrashReport, AnalyticsCapability.
  • GDPR/CCPA: granular consent persisted across restarts; pseudonymous, user-resettable client id (resetClientId()); no hardware identifiers.
  • Providers: CodenameOneAnalyticsProvider (first-party, batched to the cloud), GoogleAnalyticsProvider (GA4 Measurement Protocol), MatomoAnalyticsProvider (privacy-first, non-Google), FirebaseAnalyticsProvider, LoggingAnalyticsProvider.
  • The old AnalyticsService is retained, deprecated, and delegates to the new API (its init switches the facade to opt-out to preserve historical behaviour). UIBuilder analytics hook unchanged.

Firebase native + builders

  • Android peer (NativeFirebaseAnalyticsImpl) via reflection (mirrors the port's FCM pattern); iOS peer as an Objective-C class (com_codename1_analytics_NativeFirebaseAnalyticsImpl) using dynamic dispatch on FIRAnalytics.
  • AndroidGradleBuilder / IPhoneBuilder inject the Firebase Gradle dep / Firebase/Analytics pod, gated on the android.firebaseAnalytics / ios.firebaseAnalytics build hints (mirrors the existing FCM/AdMob handling).

Docs + tests

  • New docs/developer-guide/Analytics.asciidoc chapter (old section replaced with a pointer); passes asciidoctor + Vale + paragraph-cap locally.
  • 25 unit tests in maven/core-unittests (consent gating, all providers, client id, capabilities, deprecated facade) — all green against a from-source core build.

Verification

  • Client tests: mvn -o -pl core-unittests -DunitTests=true -Plocal-dev-javase test -Dtest='*Analytics*' → 25/25 pass.
  • iOS native peer: full local xcodebuild -sdk iphoneos -arch arm64 of a sample app → BUILD SUCCEEDED, peer object compiled and linked.
  • Builders + plugin compile cleanly.

The BuildCloud backend (ingest, tier-gated reports, console UI) and the BuildDaemon builder twin are in separate PRs on those repos.

🤖 Generated with Claude Code

…e backends

Replace the deprecated Google Analytics v1 AnalyticsService with a generic
provider SPI. Apps register one or more AnalyticsProvider implementations with
the Analytics facade, which fans screen / event / user-property / crash calls
out to all providers after a configurable (opt-in by default) consent gate.

Providers: CodenameOneAnalyticsProvider (first-party, batched to the cloud),
GoogleAnalyticsProvider (GA4), MatomoAnalyticsProvider (privacy-first, non
Google), FirebaseAnalyticsProvider (Android + iOS native peers), and
LoggingAnalyticsProvider (simulator / tests).

The old AnalyticsService is retained, deprecated, and now delegates to the new
API. Adds GDPR features (granular consent persisted across restarts,
pseudonymous user-resettable client id), Firebase build-hint dependency
injection in the Android (android.firebaseAnalytics) and iOS
(ios.firebaseAnalytics) builders, a new developer-guide Analytics chapter, and
25 unit tests. The iOS native peer was verified with a full local arm64
xcodebuild (BUILD SUCCEEDED).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 136 screenshots: 136 matched.

Native Android coverage

  • 📊 Line coverage: 14.45% (8847/61218 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 11.73% (43625/372002), branch 5.19% (1814/34981), complexity 6.19% (2074/33517), method 10.71% (1678/15663), class 17.49% (388/2218)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 14.45% (8847/61218 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 11.73% (43625/372002), branch 5.19% (1814/34981), complexity 6.19% (2074/33517), method 10.71% (1678/15663), class 17.49% (388/2218)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 215ms / native 83ms = 2.5x speedup
SIMD float-mul (64K x300) java 182ms / native 56ms = 3.2x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 156.000 ms
Base64 CN1 decode 629.000 ms
Base64 native encode 880.000 ms
Base64 encode ratio (CN1/native) 0.177x (82.3% faster)
Base64 native decode 1162.000 ms
Base64 decode ratio (CN1/native) 0.541x (45.9% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog

shai-almog commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 128 screenshots: 128 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog

shai-almog commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 134 screenshots: 134 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 143 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 54ms / native 2ms = 27.0x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 430.000 ms
Base64 CN1 decode 203.000 ms
Base64 native encode 1146.000 ms
Base64 encode ratio (CN1/native) 0.375x (62.5% faster)
Base64 native decode 297.000 ms
Base64 decode ratio (CN1/native) 0.684x (31.6% faster)
Base64 SIMD encode 54.000 ms
Base64 encode ratio (SIMD/CN1) 0.126x (87.4% faster)
Base64 SIMD decode 47.000 ms
Base64 decode ratio (SIMD/CN1) 0.232x (76.8% faster)
Base64 encode ratio (SIMD/native) 0.047x (95.3% faster)
Base64 decode ratio (SIMD/native) 0.158x (84.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 18.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.111x (88.9% faster)
Image applyMask (SIMD off) 81.000 ms
Image applyMask (SIMD on) 43.000 ms
Image applyMask ratio (SIMD on/off) 0.531x (46.9% faster)
Image modifyAlpha (SIMD off) 99.000 ms
Image modifyAlpha (SIMD on) 49.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.495x (50.5% faster)
Image modifyAlpha removeColor (SIMD off) 122.000 ms
Image modifyAlpha removeColor (SIMD on) 80.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.656x (34.4% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 211 screenshots: 211 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 135 screenshots: 135 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 328 seconds

Build and Run Timing

Metric Duration
Simulator Boot 99000 ms
Simulator Boot (Run) 0 ms
App Install 13000 ms
App Launch 4000 ms
Test Execution 383000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 58ms / native 5ms = 11.6x speedup
SIMD float-mul (64K x300) java 66ms / native 3ms = 22.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 384.000 ms
Base64 CN1 decode 202.000 ms
Base64 native encode 1667.000 ms
Base64 encode ratio (CN1/native) 0.230x (77.0% faster)
Base64 native decode 474.000 ms
Base64 decode ratio (CN1/native) 0.426x (57.4% faster)
Base64 SIMD encode 57.000 ms
Base64 encode ratio (SIMD/CN1) 0.148x (85.2% faster)
Base64 SIMD decode 50.000 ms
Base64 decode ratio (SIMD/CN1) 0.248x (75.2% faster)
Base64 encode ratio (SIMD/native) 0.034x (96.6% faster)
Base64 decode ratio (SIMD/native) 0.105x (89.5% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 75.000 ms
Image createMask (SIMD on) 42.000 ms
Image createMask ratio (SIMD on/off) 0.560x (44.0% faster)
Image applyMask (SIMD off) 210.000 ms
Image applyMask (SIMD on) 220.000 ms
Image applyMask ratio (SIMD on/off) 1.048x (4.8% slower)
Image modifyAlpha (SIMD off) 192.000 ms
Image modifyAlpha (SIMD on) 109.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.568x (43.2% faster)
Image modifyAlpha removeColor (SIMD off) 210.000 ms
Image modifyAlpha removeColor (SIMD on) 179.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.852x (14.8% faster)

@shai-almog

shai-almog commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator Author

iOS screenshot updates

Compared 131 screenshots: 130 matched, 1 updated.

  • landscape — updated screenshot. Screenshot differs (1179x2556 px, bit depth 8).

    landscape
    Preview info: JPEG preview quality 70; JPEG preview quality 70; downscaled to 825x1789.
    Full-resolution PNG saved as landscape.png in workflow artifacts.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 247 seconds

Build and Run Timing

Metric Duration
Simulator Boot 64000 ms
Simulator Boot (Run) 1000 ms
App Install 10000 ms
App Launch 1000 ms
Test Execution 454000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 55ms / native 4ms = 13.7x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 603.000 ms
Base64 CN1 decode 391.000 ms
Base64 native encode 817.000 ms
Base64 encode ratio (CN1/native) 0.738x (26.2% faster)
Base64 native decode 657.000 ms
Base64 decode ratio (CN1/native) 0.595x (40.5% faster)
Base64 SIMD encode 113.000 ms
Base64 encode ratio (SIMD/CN1) 0.187x (81.3% faster)
Base64 SIMD decode 92.000 ms
Base64 decode ratio (SIMD/CN1) 0.235x (76.5% faster)
Base64 encode ratio (SIMD/native) 0.138x (86.2% faster)
Base64 decode ratio (SIMD/native) 0.140x (86.0% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 20.000 ms
Image createMask (SIMD on) 92.000 ms
Image createMask ratio (SIMD on/off) 4.600x (360.0% slower)
Image applyMask (SIMD off) 122.000 ms
Image applyMask (SIMD on) 258.000 ms
Image applyMask ratio (SIMD on/off) 2.115x (111.5% slower)
Image modifyAlpha (SIMD off) 176.000 ms
Image modifyAlpha (SIMD on) 269.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.528x (52.8% slower)
Image modifyAlpha removeColor (SIMD off) 194.000 ms
Image modifyAlpha removeColor (SIMD on) 160.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.825x (17.5% faster)

shai-almog and others added 3 commits June 20, 2026 17:43
The CN1 core (CodenameOne/src) compiles against the CLDC11 bootclasspath,
whose StringBuilder has no substring(int,int). Convert to String first in
GoogleAnalyticsProvider.sanitizeName so the core/CLDC11 build compiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deprecated AnalyticsService now delegates to Analytics, leaving domain /
timeout / readTimeout written but never read -- SpotBugs failed the JDK 8 gate
on URF_UNREAD_FIELD. Remove the fields; the setters become documented no-ops
and the init domain parameter is retained for source compatibility only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make Analytics final (ClassWithOnlyPrivateConstructorsShouldBeFinal) and drop
the redundant public modifiers on NativeFirebaseAnalytics interface methods
(UnnecessaryModifier). Verified against the full forbidden PMD rule set via
mvn verify -- zero forbidden violations across the analytics sources.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

CI LanguageTool (rendered-HTML gate, not runnable locally) flagged British
spellings 'behavioural'/'anonymisation' and the proper noun 'Piwik'. Switch to
US spelling (behavioral/anonymization, plus honor) and accept-list Piwik.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog shai-almog force-pushed the analytics-spi-rewrite branch from f4a7d53 to 2422a15 Compare June 20, 2026 19:14
shai-almog and others added 9 commits June 21, 2026 07:55
The CodenameOneAnalyticsProvider batch now carries a per-run sessionId and
device segmentation fields (deviceModel, deviceManufacturer, formFactor,
density, screenWidth/Height, network, language) so the cloud console can do
Google-Analytics-style segmentation, sessions and screen-flow without any
hardware identifier. Values are read defensively; the model is the public
hardware string, never the user-assigned device name.

Ports expose a privacy-safe hardware model via a new DeviceHardwareModel
property: iOS adds native getDeviceHardwareModel() (sysctlbyname hw.machine,
with a simulator fallback) -- distinct from getDeviceName() which returns the
PII device name; Android maps it to Build.MODEL and adds DeviceManufacturer
(Build.MANUFACTURER).

Adds provider tests for the enriched wire format and stable per-run session id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nted Purchase/share

- Analytics.setDimension/clearDimension/getDimensions: app-defined custom
  segmentation dimensions, persisted via Preferences and sent in the batch as
  a dimensions{} object. Plus browser (coarse label from User-Agent on web)
  and osName in the batch.
- Analytics.autoEvent(): consent-gated, no-op when no providers, never throws.
  Purchase.postReceipt emits 'purchase'(commerce, sku); Display.share emits
  'share'(engagement, type) -- goals can bind to these out of the box.
- Tests for dimensions round-trip/persistence, batch dimensions+browser, and
  autoEvent fan-out/no-op.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	docs/developer-guide/languagetool-accept.txt
Analytics.getDimensions() always returns a fresh LinkedHashMap, never null,
so the 'dimensions == null' branch in appendDimensions tripped the forbidden
RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE SpotBugs rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When an app has already obtained consent through its own mechanism (custom
prompt, third-party CMP, MDM policy, or a jurisdiction where the integrator
deems consent unnecessary) it can unblock reporting in one line:
Analytics.setConsent(AnalyticsConsent.all()). Adds all()/none() static
factories (granted()/denied() kept as aliases) plus Builder.all()/none()
so a single category can be selectively revoked, e.g.
builder().all().adStorage(false). Consent gating stays opt-in by default;
this just makes the established escape hatch obvious.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Developer guide consent section now leads with AnalyticsConsent.all(),
shows builder().all().adStorage(false), adds a 'Consent you already hold'
subsection covering app-managed consent and OPT_OUT, and explains that the
data is pseudonymous (not anonymous) so opt-in is the safe default. Passes
asciidoctor lint, paragraph-cap, Vale (0) and LanguageTool (0) locally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… 'non-Google'

- Analytics.crash javadoc + developer guide now spell out that it emits a
  lightweight exception event to analytics backends and is independent of
  Codename One Crash Protection (com.codename1.crash.CrashProtection), the
  dedicated symbolicated crash pipeline -- use either or both.
- Developer guide intro now points readers to the Consent and privacy
  section (new [#analytics-consent] anchor) instead of a bare clause.
- MatomoAnalyticsProvider javadoc drops the 'non-Google' wording.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NativeInterface exists for the build server to generate per-app native
peers; that is the wrong fit for a framework-shipped provider. Replace it
with FirebaseAnalyticsProvider.Bridge -- a plain interface the build
registers via registerBridge() before startup. The provider is a safe
no-op until a bridge is registered (simulator/desktop, or a build without
Firebase). Removes the NativeFirebaseAnalytics interface, the reflection-
based Android peer and the iOS native peer; the per-platform bridge
implementations are now generated by the build (Android: direct SDK
calls; iOS: native methods). Core + analytics unit tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When android.firebaseAnalytics / ios.firebaseAnalytics is set, the build now
generates a FirebaseAnalyticsProvider.Bridge and registers it in the app Stub
before init():
- Android: FirebaseAnalyticsBridgeImpl.java calling the FirebaseAnalytics SDK
  directly (no reflection), emitted next to the Stub.
- iOS: FirebaseAnalyticsBridgeImpl.java with native methods + a generated
  cn1_firebase_analytics_bridge.m that invokes FIRAnalytics dynamically (so it
  links without the pod; no-op when absent).
Replaces the removed NativeFirebaseAnalytics native interface. Maven plugin
compiles; twin change in BuildDaemon #130.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 133 screenshots: 133 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

shai-almog and others added 3 commits June 23, 2026 21:39
The Firebase config-file locations were old Ant paths (native/android,
native/ios). Use the Maven module resource paths instead
(android/src/main/resources/google-services.json,
ios/src/main/resources/GoogleService-Info.plist), and spell out that both
the config file and the build hint are required, that the build generates
the native bridge, and that Firebase only reports from a native device
build -- never the simulator or desktop run, where the provider no-ops.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
google-services.json and the push icon go in android/src/main/resources in
a Maven project, not the old Ant native/android directory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Social Login fires login / login_failed (with the provider in 'service')
  from the central LoginCallBackProxy.
- Purchase.purchase() is now a template method that fires purchase_initiated
  then calls a new protected purchaseImpl() (the platform ZoozPurchase impls
  override that). Pairs with the existing 'purchase' completion event so the
  console can show checkout drop-off. Non-breaking: the public purchase()
  signature is unchanged and custom subclasses still work.
- Documents the automatic events (purchase_initiated, purchase, login,
  login_failed, share) in the developer guide.
- Adds AnalyticsPurchaseFunnelTest (consent-gated; reaches purchaseImpl).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant