feat: [SDK-5088] add device gesture that marks the user as a test user - #2727
Draft
nan-li wants to merge 1 commit into
Draft
feat: [SDK-5088] add device gesture that marks the user as a test user#2727nan-li wants to merge 1 commit into
nan-li wants to merge 1 commit into
Conversation
Contributor
📊 Diff Coverage ReportDiff Coverage Report (Changed Lines Only)Gate: aggregate coverage on changed executable lines must be ≥ 80% (JaCoCo line data for lines touched in the diff). Changed Files Coverage
Overall (aggregate gate)0/59 touched executable lines covered (0.0% — requires ≥ 80%) Per-file detail (informational; gate is aggregate above):
❌ Coverage Check FailedAggregate coverage on touched lines is 0.0% (minimum 80%). |
Backgrounding and foregrounding the app 6 times within 30 seconds sends an Update User PATCH setting the test_user_name property to the device-local time, so the dashboard can surface the device as a test user. The request goes straight through IHttpClient, bypassing the operation repo; a queued operation can replay hours later, and a stale raise-hand signal is worse than a failed one. The JWT rides along via the same JwtTokenStore lookup the update-user executors use. Cycles are counted on a monotonic clock. A cycle needs a real background phase of at least 250ms, which filters the synthetic rotation unfocus/focus pair from ApplicationService.onOrientationChanged, and the 30s sliding window is the only rate rule. Each counted cycle logs at verbose so manual testing can watch progress. The gesture skips when privacy consent is withheld or the user does not exist on the backend yet, and adding sdk_device_gesture_disabled to an app's enabled feature keys turns it off remotely. The raw ConfigModel.sdkRemoteFeatureFlags list is checked instead of IFeatureManager because the KMP catalog hides unregistered keys. CoreModule.register moved its misconfigured-fallback block into a helper to stay under detekt's LongMethod cap after the new registration.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
One Line Summary
Six background/foreground cycles within 30 seconds mark the current user as a test user.
Details
Motivation
Finding your own device in the dashboard is the slowest part of sending a first test push. Today that means pulling a subscription ID out of verbose logs, or searching by external ID. This adds a gesture anyone can perform on a production build, with no app changes and no new permissions. Background and foreground the app six times within 30 seconds and the SDK sets a
test_user_nameproperty on the user, which the dashboard can then surface.Scope
DeviceGestureDetectorregisters as anIApplicationLifecycleHandlerand counts background-to-foreground pairs on a monotonic clock. A cycle needs a background phase of at least 250ms. That floor filters the synthetic unfocus/focus pairApplicationService.onOrientationChangedfires when an activity declaring orientation inconfigChangesrotates, which would otherwise let six rotations fake the gesture. The 30 second sliding window is the only rate rule, so completing it takes sustained five-second round trips. Normal app switching never gets there.On completion the SDK sends
PATCH apps/{appId}/users/by/onesignal_id/{id}withtest_user_nameset to the device-local time. It goes straight throughIHttpClientinstead of the operation repo, because a queued operation can replay hours later and a stale mark is worse than one that failed outright. The JWT comes from the sameJwtTokenStorelookup the update-user executors already use.Nothing is sent when privacy consent is withheld, when the user has no backend ID yet, or when
sdk_device_gesture_disabledis in the app's enabled feature keys. That key is an inverted kill switch, so absent means enabled and a device that has never fetched flags still has a working gesture. It reads the rawConfigModel.sdkRemoteFeatureFlagslist rather thanIFeatureManager, because the manager only resolves keys the KMP catalog registers and this key is deliberately not in the catalog. Registering it there stays optional follow-up.CoreModule.registermoved its misconfigured-fallback registrations into a private helper to stay under detekt'sLongMethodcap after the new line. No behavior change.Other
The backend does not accept
test_user_nameyet and the dashboard does not surface test users yet, both tracked separately. Until that lands this ships inert, which is why it is a draft.Testing
Unit testing
DeviceGestureDetectorTestsdrives synthetic focus/unfocus sequences against a controlled clock and a mockedIHttpClient, asserting the request path, the payload, and the JWT header. Fifteen tests cover the counting rules, including six cycles firing exactly once, five firing nothing, the detector re-arming afterward, sub-250ms blips not counting, cycles spread wider than the window never accumulating six, and a long pause mid-gesture keeping earlier progress. The rest cover every skip condition.Manual testing
Not yet exercised on an Android device. The iOS counterpart was hand-tested, and that testing is what drove the timing rules here from a stricter dwell fingerprint down to the single window rule, since the strict version proved nearly impossible to perform by hand.
Affected code checklist
Checklist
Overview
Testing
Final pass
🤖 Generated with Claude Code