SIGN IN SIGN UP

fix(feedback): Improve shake detection sensitivity (#5366)

* fix(feedback): Improve shake detection sensitivity

Replace the threshold-counting approach (2.7g, 2 spikes in 1.5s) with
a rolling sample window based on Square's Seismic library. A shake is
now detected when >75% of accelerometer readings in a 0.5s window
exceed 13 m/s² (~1.33g), which works reliably on budget devices with
less sensitive accelerometers.

Fixes GH-5331

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Add license attribution for Square's Seismic library

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Add third-party code attribution guidelines to AGENTS.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Format code

* docs(changelog): Add shake detection fix entry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(feedback): Clear message field when form is re-shown via shake

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(feedback): Synchronize SampleQueue access across threads

stop() runs on the main thread while onSensorChanged() runs on the
background HandlerThread. Without synchronization, concurrent access
to the linked list and object pool can corrupt next-pointers and
cause clear() to loop forever.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ref(feedback): Replace synchronized block with handler.post for queue clear

Post queue.clear() to the HandlerThread instead of synchronizing every
sensor event. All queue access now stays single-threaded with zero
lock contention. quitSafely() drains pending messages before exiting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* dont use method ref

* ref(feedback): Remove queue.clear() from stop(), rely on timestamp purge

Sensor events are delivered via fd callbacks, not Handler messages, so
posting clear() to the HandlerThread doesn't guarantee ordering with
new events after re-registration. The SampleQueue already purges stale
samples by timestamp in add(), so explicit clearing on stop is
unnecessary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ref(feedback): Restore handler.post(clear) in stop()

Both fd callbacks and posted Messages are serialized by the Looper,
so there is no concurrent access risk. Explicit clear is cleaner than
relying on timestamp purge alone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(feedback): Require minimum sample count before triggering shake

The 75% bit-shift formula degrades at low sample counts (e.g. 33% at
3 samples). With SENSOR_DELAY_NORMAL (~5Hz) the queue may hold only
3 samples in 0.5s. Adding a MIN_QUEUE_SIZE guard ensures the threshold
stays accurate and prevents false triggers from walking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
R
Roman Zavarnitsyn committed
e3e78e1c6cc641228dd910ee39c28ed6cf1ee710
Parent: d25ef95
Committed by GitHub <noreply@github.com> on 5/5/2026, 12:37:35 PM