Closed Test SDK

Integrate the SDK into anyapp — the APK your testers install. The SDK sends activity to Dozenflow and registers your test channel on first launch. Organizers manage connections and proof in the Dozenflow app.

Library: com.groundspaceteam:closed-test-sdk · latest 0.2.21 · API: io.closedtest.sdk · Ingest: https://api.groundspaceteam.com

Quick start

  1. Add the Maven dependency to anyapp.
  2. Initialize with three required fields: owner email, Google Group join URL, and Play invite link.
  3. Ship a test build and open it once — the server receives POST /v1/init and your test appears in Dozenflow.
dependencies {
    implementation("com.groundspaceteam:closed-test-sdk:0.2.21")
}

ClosedTest.initialize(
    context = applicationContext,
    init = ClosedTestInit(
        ownerEmail = "dev@example.com",
        googleGroupUrl = "https://groups.google.com/g/your-closed-testers",
        inviteLink = "https://play.google.com/apps/testing/com.example.app",
    ),
    options = ClosedTestOptions(),
)

Call ClosedTest.initialize from Application.onCreate (or disable auto-init and call from your entry Activity). Current release: 0.2.21 on Maven Central. Release notes: SDK repository.

Required init fields

SDK ≥ 0.2.21 requires all three fields on every init path (code init or manifest auto-init). If any is missing or blank, auto-init is skipped and telemetry for mutual / connections will not start.

They are sent in POST /v1/init as owner_email, google_group_url, and invite_link. They are not copied into event payloads.

FieldWhere to get itWhy Dozenflow needs it
Owner email
ownerEmail / io.closedtest.sdk.owner_email
The email of the person who owns this closed test — usually the same address you use to sign in to Dozenflow, or a dedicated inbox for the project (e.g. dev@yourstudio.com). Server-side account identity: match or create the organizer account, attach the test card, and route mutual invites / connections to the right person. In the mutual network the same email is also the tester identity after login.
Google Group join URL
googleGroupUrl / io.closedtest.sdk.google_group_url
In Google Play Console: Testing → Closed testing (your track) → tester access uses a Google Group. Create or open the group in Google Groups and copy the public join link, e.g. https://groups.google.com/g/your-closed-testers. Play closed tests often require testers to join a Google Group before they can opt in. Dozenflow stores this link on the test card and shows it in Connections so reciprocal testers know the exact join step.
Play invite link
inviteLink / io.closedtest.sdk.invite_link
In Google Play Console: Testing → Closed testing → open your track → copy the link to share with testers (opt-in URL). Format: https://play.google.com/apps/testing/<applicationId>. How testers install your build from Play. Stored on the test card and reused in Connections / onboarding — no manual copy-paste from chat when someone accepts your mutual test.

Tip: after the first successful init, open Dozenflow → your test → Connections to confirm the Group and invite links look correct. You can adjust them in the app if Play Console links change.

Manifest auto-init (optional)

Instead of calling ClosedTest.initialize in code, you can set the three required fields as meta-data. AndroidX App Startup runs when the dependency is on the classpath.

<application ...>
    <meta-data
        android:name="io.closedtest.sdk.owner_email"
        android:value="dev@example.com" />
    <meta-data
        android:name="io.closedtest.sdk.google_group_url"
        android:value="https://groups.google.com/g/your-closed-testers" />
    <meta-data
        android:name="io.closedtest.sdk.invite_link"
        android:value="https://play.google.com/apps/testing/com.example.app" />
</application>
  • Backend URL is fixed inside the SDK — do not configure it in app code.
  • Missing or blank any of the three fields → auto-init is skipped.
  • Duplicate ClosedTest.initialize(...) calls are ignored.

If you only want code init (Quick start), disable auto-init:

<meta-data
    android:name="io.closedtest.sdk.auto_init_enabled"
    android:value="false" />

ClosedTestOptions

OptionPurpose
heartbeatIntervalMsForeground heartbeat interval
backgroundSessionEndDelayMsDelay before session_end after background
collectInDebuggableBuildsCollect telemetry in debug builds
okHttpClientCustom OkHttp client
maxQueuedEventsRoom queue cap
eventsBatchSizeBatch upload size
uploadBackoffInitialMs / uploadBackoffMaxMsRetry backoff
proofFlowHintEnabledShow “open organizer app” dialog after init (default true)
proofFlowHintMaxShowsMax dialog shows per install (default 3)
proofFlowHintCooldownMsCooldown after “Later” (default 7 days)
proofFlowPackageNamesPackages checked for the installed Dozenflow app
dailyReminderEnabledLocal daily notification when the app was not opened today (default true)
dailyReminderHourLocalReminder hour, device timezone (default 15)
dailyReminderMinuteLocalReminder minute (default 0)

Manual events

ClosedTest.trackScreen("Home")
ClosedTest.trackInteraction("tap")
ClosedTest.trackEvent("onboarding_done", mapOf("step" to "2"))
ClosedTest.flush()

Navigation funnels: screen flow guide (screen_view).

Tester binding

ClosedTest.handleDeepLink(intent?.data)

ClosedTest.bindTester(
    testerId = "…",
    testSessionId = "…",
)

Organizer app hint dialog

When POST /v1/init returns proofflow_test_id and hints are enabled, the SDK may offer to open Dozenflow: proofflow://test/{proofflow_test_id}.

Local daily test reminder (not FCM)

Local notification on the tester device — no Firebase in anyapp. Default 15:00 local time if the app was not opened yet today. Requires POST_NOTIFICATIONS on Android 13+.

Discovery marker

Exported ContentProvider at {applicationId}.closedtest.discovery so Dozenflow can verify the SDK is installed in a specific package.

What runs automatically

  • POST /v1/init with owner email + Group + invite on first launch
  • Lifecycle: session_start, session_end, foreground/background
  • Foreground heartbeat, Room queue, batched upload
  • Token refresh on HTTP 401

After SDK init

  1. Install Dozenflow and sign in with the same owner email (or link the account).
  2. Open your test — it should appear after the first successful SDK init from anyapp.
  3. Check Connections: Group join URL and Play invite link from init should match what testers need.
  4. Distribute the build; sessions and campaign signals appear when testers open anyapp.

Troubleshooting

SymptomCheck
SDK never starts / no init All three required fields set and non-blank; check Logcat for auto-init skip; try explicit ClosedTestInit
Test missing in Dozenflow Successful POST /v1/init; owner email matches signed-in account; anyapp opened at least once on a device
Wrong Group or invite in Connections Update values in Play Console, ship new build with corrected init, or edit on test card in Dozenflow if shown
No events on server Network; 403/401 on init; wrong package on the test card; anyapp never opened after shipping the SDK

Further reference