feat(feedback): implement shake gesture detection#5150
Merged
Conversation
Adds SentryShakeDetector (accelerometer-based) and ShakeDetectionIntegration that shows the feedback dialog when a shake is detected. Controlled by SentryFeedbackOptions.useShakeGesture (default false). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7 tasks
Contributor
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛
Internal Changes 🔧Deps
Other
🤖 This preview updates automatically when you update the PR. |
Contributor
|
Contributor
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 8687935 | 332.52 ms | 362.23 ms | 29.71 ms |
| 27d7cf8 | 309.43 ms | 364.27 ms | 54.85 ms |
| f064536 | 329.00 ms | 395.62 ms | 66.62 ms |
| abfcc92 | 337.38 ms | 427.39 ms | 90.00 ms |
| ae7fed0 | 293.84 ms | 380.22 ms | 86.38 ms |
| d15471f | 361.89 ms | 378.07 ms | 16.18 ms |
| 1df7eb6 | 397.04 ms | 429.64 ms | 32.60 ms |
| 9fbb112 | 401.87 ms | 515.87 ms | 114.00 ms |
| 6405ec5 | 310.88 ms | 354.56 ms | 43.69 ms |
| 62b579c | 318.48 ms | 367.71 ms | 49.24 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 8687935 | 1.58 MiB | 2.19 MiB | 619.17 KiB |
| 27d7cf8 | 1.58 MiB | 2.12 MiB | 549.42 KiB |
| f064536 | 1.58 MiB | 2.20 MiB | 633.90 KiB |
| abfcc92 | 1.58 MiB | 2.13 MiB | 557.31 KiB |
| ae7fed0 | 1.58 MiB | 2.12 MiB | 551.77 KiB |
| d15471f | 1.58 MiB | 2.13 MiB | 559.54 KiB |
| 1df7eb6 | 1.58 MiB | 2.10 MiB | 532.97 KiB |
| 9fbb112 | 1.58 MiB | 2.11 MiB | 539.18 KiB |
| 6405ec5 | 1.58 MiB | 2.12 MiB | 552.23 KiB |
| 62b579c | 0 B | 0 B | 0 B |
Previous results on branch: antonis/feedback-shake
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 90c01e9 | 314.38 ms | 370.51 ms | 56.13 ms |
| 395b61e | 318.16 ms | 373.86 ms | 55.70 ms |
| 2052d5f | 331.52 ms | 401.71 ms | 70.19 ms |
| dc10a90 | 307.28 ms | 359.06 ms | 51.78 ms |
| d743634 | 404.69 ms | 507.35 ms | 102.66 ms |
| 3a14925 | 327.15 ms | 380.52 ms | 53.37 ms |
| ba57364 | 317.45 ms | 360.35 ms | 42.90 ms |
| 1229590 | 312.98 ms | 355.29 ms | 42.31 ms |
| c52d557 | 383.26 ms | 436.00 ms | 52.74 ms |
| 58a9026 | 330.31 ms | 370.45 ms | 40.13 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 90c01e9 | 1.58 MiB | 2.29 MiB | 727.37 KiB |
| 395b61e | 1.58 MiB | 2.29 MiB | 727.32 KiB |
| 2052d5f | 1.58 MiB | 2.29 MiB | 726.85 KiB |
| dc10a90 | 0 B | 0 B | 0 B |
| d743634 | 0 B | 0 B | 0 B |
| 3a14925 | 1.58 MiB | 2.29 MiB | 727.39 KiB |
| ba57364 | 1.58 MiB | 2.29 MiB | 727.15 KiB |
| 1229590 | 0 B | 0 B | 0 B |
| c52d557 | 0 B | 0 B | 0 B |
| 58a9026 | 1.58 MiB | 2.29 MiB | 727.33 KiB |
- Add volatile/AtomicLong for thread-safe cross-thread field access - Use SystemClock.elapsedRealtime() instead of System.currentTimeMillis() - Use SENSOR_DELAY_NORMAL for better battery efficiency - Add multi-shake counting (2+ threshold crossings within 1.5s window) - Handle deferred init for already-resumed activities - Wrap showDialog() in try-catch to prevent app crashes - Improve activity transition handling in onActivityPaused - Mark SentryShakeDetector as @ApiStatus.Internal - Add unit tests for SentryShakeDetector and ShakeDetectionIntegration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
… shakes Track dialog visibility with an isDialogShowing flag that is set before showing and cleared via the onFormClose callback when the dialog is dismissed. Double-checked on both sensor and UI threads to avoid races. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/ShakeDetectionIntegration.java
Outdated
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
… growth Save the user's original onFormClose once during register() and restore it after each dialog dismiss, instead of wrapping it with a new lambda each time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Outdated
Show resolved
Hide resolved
…ck flag If showDialog silently fails (e.g. activity destroyed between post and execution), isDialogShowing would stay true forever, permanently disabling shake-to-feedback. Reset it in onActivityPaused since the dialog cannot outlive its host activity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
…ActivityDestroyed AlertDialog survives pause/resume cycles (e.g. screen off/on), so resetting isDialogShowing in onActivityPaused allowed duplicate dialogs. Move the reset to onActivityDestroyed where the dialog truly cannot survive. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
…back on error - Only reset isDialogShowing in onActivityDestroyed when it's the activity that hosts the dialog, not any unrelated activity. - Restore originalOnFormClose in the catch block when showDialog throws. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java
Outdated
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Outdated
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Outdated
Show resolved
Hide resolved
Co-authored-by: LucasZF <lucas-zimerman1@hotmail.com>
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Outdated
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Outdated
Show resolved
Hide resolved
Sentry Build Distribution
|
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Show resolved
Hide resolved
The onFormClose lambda was reading previousOnFormClose field at dismiss time. If onActivityResumed or onActivityDestroyed already restored and nulled the field, the lambda would overwrite onFormClose with null. Now captured as a local variable at dialog creation time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sentry Build Distribution
|
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
When close() is called while a dialog is showing, lifecycle callbacks are unregistered so onActivityDestroyed cleanup won't fire. Restore previousOnFormClose and reset dialog state in close() to prevent the callback from being permanently overwritten. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Show resolved
Hide resolved
Sentry Build Distribution
|
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Show resolved
Hide resolved
Add proactive activity validity check inside the runOnUiThread lambda to avoid hitting the catch block with a BadTokenException when the activity becomes invalid between the shake callback and UI execution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Show resolved
Hide resolved
romtsn
reviewed
Mar 17, 2026
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Outdated
Show resolved
Hide resolved
romtsn
reviewed
Mar 17, 2026
sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java
Outdated
Show resolved
Hide resolved
romtsn
approved these changes
Mar 17, 2026
Member
romtsn
left a comment
There was a problem hiding this comment.
Great effort, thanks for addressing many PR reviews! There's only one left for reusing the handler thread, but LGTM otherwise
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java
Show resolved
Hide resolved
Follow the established defensive pattern used by all other Android integrations instead of an unchecked cast that could throw ClassCastException if a hybrid SDK passes a different options type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
Implements shake gesture detection for the user feedback form. When
useShakeGestureis enabled inSentryFeedbackOptions, shaking the device opens the feedback form.The implementation uses the device's accelerometer (via
SensorManager) with a 2.7G threshold and 1-second cooldown. A newShakeDetectionIntegrationregisters lifecycle callbacks to start/stop the detector when the activity resumes/pauses.Note: this is exposed to be used on React Native too getsentry/sentry-react-native#5754
💡 Motivation and Context
The
useShakeGestureproperty already existed inSentryFeedbackOptionsbut was never wired up. This PR implements the feature. The implementation is placed here (sentry-java) rather than in each SDK separately so it can be reused by all SDKs that embed the feedback UI (React Native, Flutter, .NET MAUI, Unity).No special permissions are required —
TYPE_ACCELEROMETERis not a protected sensor. OnlyBODY_SENSORSrequires a permission.💚 How did you test it?
SentryShakeDetectorandShakeDetectionIntegration📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
sentry-react-native will remove its own
RNSentryShakeDetectorand delegate to this class instead.