Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/smooth-birds-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"posthog": minor
"posthog-android": minor
"posthog-android-surveys-compose": minor
---

Support survey partial response collection. When enabled, submit cumulative answers after each question with a stable submission ID and completion status, matching posthog-js.

Persist unfinished survey progress across app restarts and restore the submission ID, collected answers, and next question. Clear progress on completion, dismissal, SDK reset, and incompatible survey updates. The Compose renderer starts at the restored question.

Keep unfinished surveys across Activity teardown, preserve unreadable progress during Direct Boot, and invalidate delayed responses on reset without mixing user identities.

Discard visible, delayed, and retained Compose survey input on reset, while preserving fresh presentations and delegate reuse across SDK configurations.

Preserve unfinished progress when startup has no cached survey configuration; confirmed empty survey lists still clear removed surveys.
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ jobs:
if: needs.detect-markdown-only.outputs.markdown_only != 'true'
run: make compile

- name: Test survey UI
if: needs.detect-markdown-only.outputs.markdown_only != 'true'
run: make testSurveyUI

- name: Check release tasks and dependency locks
if: needs.detect-markdown-only.outputs.markdown_only != 'true'
run: make checkRelease
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: clean compile stop checkFormat format api dryRelease release testReport test testJava generateLintBaseLine checkRelease updateLocks
.PHONY: clean compile stop checkFormat format api dryRelease release testReport test testJava testSurveyUI generateLintBaseLine checkRelease updateLocks

clean:
./gradlew clean
Expand Down Expand Up @@ -67,6 +67,10 @@ test:
testJava:
./gradlew :posthog:test

# Mounted Compose tests use the debug-only test activity manifest, including on CI.
testSurveyUI:
CI=false ./gradlew :posthog-android-surveys-compose:testDebugUnitTest

generateLintBaseLine:
rm -f posthog-android/lint-baseline.xml
./gradlew lintDebug -Dlint.baselines.continue=true
Expand Down
8 changes: 8 additions & 0 deletions posthog-android-surveys-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ android {
}
}

testOptions {
unitTests.isIncludeAndroidResources = true
}

buildFeatures {
compose = true
}
Expand Down Expand Up @@ -89,6 +93,10 @@ dependencies {
debugImplementation("androidx.compose.ui:ui-tooling")

// tests
testImplementation("androidx.test.ext:junit:${PosthogBuildConfig.Dependencies.ANDROIDX_JUNIT}")
testImplementation("org.robolectric:robolectric:${PosthogBuildConfig.Dependencies.ROBOLECTRIC}")
testImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-test-manifest")
testImplementation("junit:junit:${PosthogBuildConfig.Dependencies.ANDROIDX_JUNIT}")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:${PosthogBuildConfig.Kotlin.KOTLIN}")
}
Expand Down
65 changes: 58 additions & 7 deletions posthog-android-surveys-compose/gradle.lockfile

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import com.posthog.surveys.OnPostHogSurveyClosed
import com.posthog.surveys.OnPostHogSurveyResponse
import com.posthog.surveys.OnPostHogSurveyShown
import com.posthog.surveys.PostHogDisplaySurvey
import com.posthog.surveys.PostHogSurveysDelegate
import com.posthog.surveys.PostHogSurveyPresentation
import com.posthog.surveys.PostHogSurveyPresentationSession
import com.posthog.surveys.PostHogSurveysConfig
import com.posthog.surveys.PostHogSurveysResetAwareDelegate

/**
* Default Compose-based UI for PostHog surveys on Android.
Expand Down Expand Up @@ -63,7 +66,7 @@ import com.posthog.surveys.PostHogSurveysDelegate
* The constructor accepts any [Context] and resolves the [Application] from
* it, so passing an activity context is safe.
*/
public class PostHogSurveysComposeDelegate(context: Context) : PostHogSurveysDelegate {
public class PostHogSurveysComposeDelegate(context: Context) : PostHogSurveysResetAwareDelegate {
private val application: Application = context.applicationContext as Application
private val activityProvider: ActivityProvider = ActivityProvider()
private val host: PostHogSurveyHost = PostHogSurveyHost(activityProvider)
Expand All @@ -86,6 +89,30 @@ public class PostHogSurveysComposeDelegate(context: Context) : PostHogSurveysDel
)
}

override fun renderSurvey(
presentation: PostHogSurveyPresentation,
onSurveyShown: OnPostHogSurveyShown,
onSurveyResponse: OnPostHogSurveyResponse,
onSurveyClosed: OnPostHogSurveyClosed,
) {
host.show(presentation, onSurveyShown, onSurveyResponse, onSurveyClosed)
}

override fun bindSurveySession(session: PostHogSurveyPresentationSession) {
host.bindSession(session)
}

override fun onSurveyReset(
resetGeneration: Long,
config: PostHogSurveysConfig,
) {
host.onReset(resetGeneration, config)
}

override fun cleanupSurveys(session: PostHogSurveyPresentationSession) {
host.cleanup(session)
}

override fun cleanupSurveys() {
host.cleanup()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal class ActivityProvider : Application.ActivityLifecycleCallbacks {

/**
* Invoked on the main thread when an activity resumes, so a survey dropped
* for a configuration change can be re-presented on the recreated activity.
* during host teardown can be re-presented on the next foreground activity.
*/
var onActivityResumedListener: ((Activity) -> Unit)? = null

Expand Down
Loading
Loading