Skip to content

Add InstallIdProvider (UUID v7 + SharedPreferences) and bump to 0.0.4 - #7

Merged
JakubMrozek merged 2 commits into
mainfrom
feat/install-id-provider
May 18, 2026
Merged

Add InstallIdProvider (UUID v7 + SharedPreferences) and bump to 0.0.4#7
JakubMrozek merged 2 commits into
mainfrom
feat/install-id-provider

Conversation

@JakubMrozek

Copy link
Copy Markdown
Member

Summary

  • New deviceinfo/InstallIdProvider.kt: a public object exposing getOrCreate(context): String, which returns a UUID v7 per-app-install identifier persisted in a dedicated kontextso SharedPreferences file under the installId key. Generated on first call, survives launches, resets only on uninstall / app-data clear.
  • Bumps kit = "0.0.3""0.0.4" and adds a 0.0.4 CHANGELOG.md entry.

Mirrors kontextkit-ios's InstallIdProvider (which reads/writes UserDefaults.standard) and the kontextso:installId localStorage key used by @kontextso/sdk-js, so web, iOS, and Android installs share the same shape on the wire.

Why now

sdk-kotlin is wiring installId into its /init, /preload, /error, and /debug request payloads as part of the v4 release (see sdk-kotlin PR #103). Threading the field on the consumer side is blocked on this kit ship — sdk-kotlin will pin to 0.0.4 once it's on Maven Central.

Design notes

  • UUID v7 (RFC 9562 §5.7): 48-bit big-endian Unix-epoch ms in bytes 0–5; version 0111 in byte 6; variant 10 in byte 8; remaining bits from SecureRandom. Time-ordered, so server-side sorts and B-tree indexes stay friendly.
  • Validator is version-agnostic: checks the canonical 8-4-4-4-12 hex shape only, not v7-specific bits. A future generator change (e.g., v8) won't invalidate IDs already on users' devices.
  • Strict shape check via regexjava.util.UUID.fromString accepts shorthand like "1-2-3-4-5", which would round-trip forever once stored.
  • Storage location: dedicated kontextso SharedPreferences file, not the host app's default prefs. Keeps the install ID out of any file the IAB TCF library reads/writes through PreferenceManager.getDefaultSharedPreferences, and gives the host app a single file name to clear if they ever need to reset the ID.

Test plan

  • ./gradlew spotlessCheck detekt test — BUILD SUCCESSFUL locally
  • 15 unit tests in InstallIdProviderTest.kt:
    • v7 shape match (canonical regex)
    • Timestamp prefix is within before..after system millis
    • Time-ordering across distinct millisecond boundaries
    • getOrCreate generates and persists on first call
    • getOrCreate returns same value on subsequent calls
    • Pre-seeded value is returned untouched
    • Forward-compat: a stored v4 UUID is accepted (version-agnostic validator)
    • 6 malformed-input overwrite cases (empty / non-UUID / truncated / extras / JSON / non-hex)
    • Android-specific: "1-2-3-4-5" shorthand is rejected (would pass UUID.fromString)
    • Direct isCanonicalUuid accept/reject sanity

🤖 Generated with Claude Code

JakubMrozek and others added 2 commits May 18, 2026 16:30
UUID v7 generator (RFC 9562) plus persistent storage in a dedicated
kontextso SharedPreferences file under the installId key. Survives
launches, resets only on uninstall / app-data clear. Mirrors iOS
InstallIdProvider (UserDefaults.standard, kontextso.installId key)
and the kontextso:installId localStorage key used by @kontextso/sdk-js
so web and native installs share the same shape on the wire.

The validator is intentionally version-agnostic — checks 8-4-4-4-12
hex shape only, not v7-specific bits — so a future generator change
doesn't invalidate IDs already on disk. Uses a strict regex rather
than java.util.UUID.fromString, which accepts shorthand like "1-2-3-4-5".

Bumps version 0.0.3 → 0.0.4. Consumer SDKs (sdk-kotlin) will pin to
0.0.4 and thread installId through their request DTOs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SecureRandom is documented thread-safe and meant to be reused; instantiating
one per uuidv7() call re-seeds from /dev/urandom every time, which is
measurably slow on older Android devices. Lift to a private val so the
seed-and-construct cost is paid once.

Also fixes a misleading claim in the class docstring. The previous wording
said this mirrors iOS's "kontextso:installId field name" — but iOS actually
stores under "kontextso.installId" (flat UserDefaults key with dot), and
sdk-js uses localStorage key "kontextso:installId" (with colon). Only the
on-the-wire DTO field name (installId) is shared across all three; the
local-storage layout is per-platform by design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JakubMrozek
JakubMrozek merged commit 4f26f27 into main May 18, 2026
1 check 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.

1 participant