Skip to content

feat(feedback): Add screenshot attachment button to user feedback widget - #5828

Open
markushi wants to merge 13 commits into
mainfrom
feat/feedback-screenshot-attachment
Open

feat(feedback): Add screenshot attachment button to user feedback widget#5828
markushi wants to merge 13 commits into
mainfrom
feat/feedback-screenshot-attachment

Conversation

@markushi

@markushi markushi commented Jul 23, 2026

Copy link
Copy Markdown
Member

📜 Description

Adds an "Add a screenshot" button to the Android user feedback form that opens the
androidx photo picker (ActivityResultContracts.PickVisualMedia, images only) and sends
the picked image as an attachment in the same envelope as the feedback.

Screen_recording_20260723_183244.mp4
image
  • New SentryFeedbackOptions: enableScreenshot (default true, matching the JS SDK),
    addScreenshotButtonLabel, removeScreenshotButtonLabel, plus the
    io.sentry.feedback.enable-screenshot manifest flag
  • androidx.activity:activity:1.8.2 is added as compileOnly — no new transitive
    dependency; the button is hidden (with a warning log) when androidx.activity is missing
    or the host Activity is not a ComponentActivity
  • The image is size-checked against maxAttachmentSize at pick time

💡 Motivation and Context

💚 How did you test it?

Unit tests + Manual testing.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.

@linear-code

linear-code Bot commented Jul 23, 2026

Copy link
Copy Markdown

JAVA-538

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 9411cc4

@sentry

sentry Bot commented Jul 23, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.52.0 (1) release

⚙️ sentry-android Build Distribution Settings

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 396.43 ms 478.04 ms 81.61 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
d8b6ce1 314.62 ms 348.67 ms 34.05 ms
0d66c0b 327.83 ms 403.40 ms 75.57 ms
fcec2f2 311.35 ms 384.94 ms 73.59 ms
c3ee041 310.64 ms 361.90 ms 51.26 ms
22f4345 313.52 ms 364.96 ms 51.44 ms
6b019b7 343.31 ms 417.23 ms 73.91 ms
b6cfb57 372.92 ms 507.77 ms 134.85 ms
b67bb28 307.59 ms 341.24 ms 33.65 ms
5865051 319.74 ms 365.60 ms 45.86 ms
80fd6ad 321.06 ms 375.79 ms 54.73 ms

App size

Revision Plain With Sentry Diff
d8b6ce1 0 B 0 B 0 B
0d66c0b 1.58 MiB 2.29 MiB 720.89 KiB
fcec2f2 1.58 MiB 2.12 MiB 551.51 KiB
c3ee041 0 B 0 B 0 B
22f4345 1.58 MiB 2.29 MiB 719.83 KiB
6b019b7 0 B 0 B 0 B
b6cfb57 1.58 MiB 2.28 MiB 718.80 KiB
b67bb28 0 B 0 B 0 B
5865051 0 B 0 B 0 B
80fd6ad 0 B 0 B 0 B

Previous results on branch: feat/feedback-screenshot-attachment

Startup times

Revision Plain With Sentry Diff
71243e7 313.69 ms 370.22 ms 56.54 ms
37c6869 327.68 ms 381.06 ms 53.38 ms
676848b 367.87 ms 443.02 ms 75.15 ms
86e5409 326.20 ms 351.31 ms 25.11 ms

App size

Revision Plain With Sentry Diff
71243e7 0 B 0 B 0 B
37c6869 0 B 0 B 0 B
676848b 0 B 0 B 0 B
86e5409 0 B 0 B 0 B

@markushi
markushi marked this pull request as ready for review July 23, 2026 17:27
Comment thread sentry/src/main/java/io/sentry/util/FileUtils.java
Comment thread sentry/src/main/java/io/sentry/util/FileUtils.java Outdated

@runningcode runningcode left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this!

mime,
"event.attachment",
false));
} catch (Throwable t) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided I'm always going to comment whenever we add new usages of this. Sorry to bring it up again.

  1. How will we know that this feature is working in production if we swallow all errors?
  2. Since we're dealing with images, files and attachments how we we know we aren't swallowing a permissions issue or OOM?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to using the newly added ExceptionUtils.rethrowIfFatal, so OOM and linkage errors propagate. getUriSize now logs the throwable too, it was silent before.

return cursor.getLong(sizeIndex);
}
}
} catch (Throwable ignored) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment here as above.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to using the newly added ExceptionUtils.rethrowIfFatal, so OOM and linkage errors propagate. getUriSize now logs the throwable too, it was silent before.

Comment thread sentry/src/main/java/io/sentry/util/FileUtils.java Outdated
Comment thread sentry-android-core/build.gradle.kts
Comment thread sentry/src/main/java/io/sentry/SentryFeedbackOptions.java Outdated
Comment thread sentry/src/main/java/io/sentry/SentryFeedbackOptions.java Outdated
markushi added 2 commits July 24, 2026 10:39
Use term "screenshot" everywhere
Improve nullability handling

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0a3d51d. Configure here.

markushi and others added 2 commits August 12, 2026 12:48
…hment paths

Routes the three catch-all blocks around the screenshot picker through
ExceptionUtils.rethrowIfFatal, so OOM, ThreadDeath and linkage errors
propagate instead of being hidden, and logs the previously silent
failure in getUriSize.

Also moves the androidx.activity availability check out of
SentryFeedbackScreenshotPicker, so the class is only loaded once the
check passed, as its javadoc requires.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment on lines +38 to +48
((ComponentActivity) activity)
.getActivityResultRegistry()
.register(
"sentry_user_feedback_screenshot_picker",
new ActivityResultContracts.PickVisualMedia(),
(@Nullable Uri uri) -> {
if (uri != null) {
callback.onScreenshotPicked(uri);
}
});
return new SentryFeedbackScreenshotPicker(launcher);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The ActivityResultRegistry.register() call for the screenshot picker is made after the Activity's lifecycle is STARTED, which will throw an unhandled IllegalStateException and crash the app.
Severity: CRITICAL

Suggested Fix

The registration of the ActivityResultLauncher should be moved to a point in the lifecycle before the host Activity is started, such as in the onCreate() method of the Activity or Fragment. If registration must happen dynamically, wrap the call in a try-catch block to handle the IllegalStateException gracefully, although this would prevent the feature from working. The recommended approach is to register early in the lifecycle.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
sentry-android-core/src/main/java/io/sentry/android/core/SentryFeedbackScreenshotPicker.java#L37-L48

Potential issue: The `SentryUserFeedbackForm` dialog attempts to register an
`ActivityResultLauncher` for the screenshot picker within its `onStart()` method. This
registration is performed on the host `ComponentActivity`, which is already in the
`STARTED` or `RESUMED` state. According to Android's `ActivityResultRegistry`
documentation, `register()` must be called before the `LifecycleOwner` reaches the
`STARTED` state. Calling it after this point throws an `IllegalStateException`. Since
the call to `SentryFeedbackScreenshotPicker.register()` is not wrapped in a `try-catch`
block, this exception is unhandled and will crash the application when the feedback form
is displayed.

Also affects:

  • sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java:385~385

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add file attachment support to user feedback widget

2 participants