From 1ef010969c563a48f809c9c32b7d3bf6f9d53990 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 29 Apr 2026 14:54:26 +0200 Subject: [PATCH 01/18] ref(feedback): Rename Dialog to Form across feedback APIs Rename SentryUserFeedbackDialog to SentryUserFeedbackForm as the primary class. Keep SentryUserFeedbackDialog as a deprecated subclass for backward compatibility. Also rename internal APIs to use Form naming consistently: - IDialogHandler -> IFormHandler - showDialog -> showForm - setDialogHandler/getDialogHandler -> setFormHandler/getFormHandler - AndroidUserFeedbackIDialogHandler -> AndroidUserFeedbackFormHandler Add deprecated Sentry.showUserFeedbackDialog() overloads that delegate to the new Sentry.showUserFeedbackForm() methods. Co-Authored-By: Claude Opus 4.6 --- .../api/sentry-android-core.api | 37 +- .../core/AndroidOptionsInitializer.java | 2 +- .../core/FeedbackShakeIntegration.java | 2 +- .../android/core/SentryAndroidOptions.java | 6 +- .../core/SentryUserFeedbackButton.java | 4 +- .../core/SentryUserFeedbackDialog.java | 360 +++-------------- .../android/core/SentryUserFeedbackForm.java | 379 ++++++++++++++++++ .../core/AndroidOptionsInitializerTest.kt | 6 +- .../core/FeedbackShakeIntegrationTest.kt | 4 +- ...gTest.kt => SentryUserFeedbackFormTest.kt} | 8 +- .../uitest/android/UserFeedbackUiTest.kt | 14 +- .../compose/SentryUserFeedbackButton.kt | 2 +- sentry/api/sentry.api | 13 +- sentry/src/main/java/io/sentry/Sentry.java | 40 +- .../java/io/sentry/SentryFeedbackOptions.java | 28 +- .../main/java/io/sentry/SentryOptions.java | 2 +- .../io/sentry/SentryFeedbackOptionsTest.kt | 8 +- .../test/java/io/sentry/SentryOptionsTest.kt | 4 +- sentry/src/test/java/io/sentry/SentryTest.kt | 32 +- 19 files changed, 560 insertions(+), 391 deletions(-) create mode 100644 sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java rename sentry-android-core/src/test/java/io/sentry/android/core/{SentryUserFeedbackDialogTest.kt => SentryUserFeedbackFormTest.kt} (94%) diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 0d83082548f..61d45783864 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -496,23 +496,44 @@ public class io/sentry/android/core/SentryUserFeedbackButton : android/widget/Bu public fun setOnClickListener (Landroid/view/View$OnClickListener;)V } -public final class io/sentry/android/core/SentryUserFeedbackDialog : android/app/AlertDialog { - public fun setCancelable (Z)V - public fun setOnDismissListener (Landroid/content/DialogInterface$OnDismissListener;)V - public fun show ()V +public final class io/sentry/android/core/SentryUserFeedbackDialog : io/sentry/android/core/SentryUserFeedbackForm { } -public class io/sentry/android/core/SentryUserFeedbackDialog$Builder { +public class io/sentry/android/core/SentryUserFeedbackDialog$Builder : io/sentry/android/core/SentryUserFeedbackForm$Builder { public fun (Landroid/content/Context;)V public fun (Landroid/content/Context;I)V - public fun (Landroid/content/Context;ILio/sentry/android/core/SentryUserFeedbackDialog$OptionsConfiguration;)V - public fun (Landroid/content/Context;Lio/sentry/android/core/SentryUserFeedbackDialog$OptionsConfiguration;)V + public fun (Landroid/content/Context;ILio/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration;)V + public fun (Landroid/content/Context;Lio/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration;)V public fun associatedEventId (Lio/sentry/protocol/SentryId;)Lio/sentry/android/core/SentryUserFeedbackDialog$Builder; + public synthetic fun associatedEventId (Lio/sentry/protocol/SentryId;)Lio/sentry/android/core/SentryUserFeedbackForm$Builder; public fun configurator (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)Lio/sentry/android/core/SentryUserFeedbackDialog$Builder; + public synthetic fun configurator (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)Lio/sentry/android/core/SentryUserFeedbackForm$Builder; public fun create ()Lio/sentry/android/core/SentryUserFeedbackDialog; + public synthetic fun create ()Lio/sentry/android/core/SentryUserFeedbackForm; +} + +public abstract interface class io/sentry/android/core/SentryUserFeedbackDialog$OptionsConfiguration : io/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration { +} + +public class io/sentry/android/core/SentryUserFeedbackForm : android/app/AlertDialog { + protected fun onCreate (Landroid/os/Bundle;)V + protected fun onStart ()V + public fun setCancelable (Z)V + public fun setOnDismissListener (Landroid/content/DialogInterface$OnDismissListener;)V + public fun show ()V +} + +public class io/sentry/android/core/SentryUserFeedbackForm$Builder { + public fun (Landroid/content/Context;)V + public fun (Landroid/content/Context;I)V + public fun (Landroid/content/Context;ILio/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration;)V + public fun (Landroid/content/Context;Lio/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration;)V + public fun associatedEventId (Lio/sentry/protocol/SentryId;)Lio/sentry/android/core/SentryUserFeedbackForm$Builder; + public fun configurator (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)Lio/sentry/android/core/SentryUserFeedbackForm$Builder; + public fun create ()Lio/sentry/android/core/SentryUserFeedbackForm; } -public abstract interface class io/sentry/android/core/SentryUserFeedbackDialog$OptionsConfiguration { +public abstract interface class io/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration { public abstract fun configure (Landroid/content/Context;Lio/sentry/SentryFeedbackOptions;)V } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 5f7fad69b5d..5704cf7d7d4 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -441,7 +441,7 @@ static void installDefaultIntegrations( } options .getFeedbackOptions() - .setDialogHandler(new SentryAndroidOptions.AndroidUserFeedbackIDialogHandler()); + .setFormHandler(new SentryAndroidOptions.AndroidUserFeedbackFormHandler()); } /** diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java index b845b6ed8c4..fc34f18152f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java @@ -178,7 +178,7 @@ private void startShakeDetection(final @NotNull Activity activity) { } previousOnFormClose = null; }); - new SentryUserFeedbackDialog.Builder(active).create().show(); + new SentryUserFeedbackForm.Builder(active).create().show(); } catch (Throwable e) { isDialogShowing = false; options.getFeedbackOptions().setOnFormClose(previousOnFormClose); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index 054e43322a2..8fe702aad50 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -741,9 +741,9 @@ public void setEnableAnrFingerprinting(final boolean enableAnrFingerprinting) { this.enableAnrFingerprinting = enableAnrFingerprinting; } - static class AndroidUserFeedbackIDialogHandler implements SentryFeedbackOptions.IDialogHandler { + static class AndroidUserFeedbackFormHandler implements SentryFeedbackOptions.IFormHandler { @Override - public void showDialog( + public void showForm( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { final @Nullable Activity activity = CurrentActivityHolder.getInstance().getActivity(); @@ -758,7 +758,7 @@ public void showDialog( return; } - new SentryUserFeedbackDialog.Builder(activity) + new SentryUserFeedbackForm.Builder(activity) .associatedEventId(associatedEventId) .configurator(configurator) .create() diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java index eedafd8f001..729dfd0b4e7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java @@ -104,7 +104,7 @@ private void init( } } - // Set the default ClickListener to open the SentryUserFeedbackDialog + // Set the default ClickListener to open the SentryUserFeedbackForm setOnClickListener(delegate); } @@ -113,7 +113,7 @@ public void setOnClickListener(final @Nullable OnClickListener listener) { delegate = listener; super.setOnClickListener( v -> { - new SentryUserFeedbackDialog.Builder(getContext()).create().show(); + new SentryUserFeedbackForm.Builder(getContext()).create().show(); if (delegate != null) { delegate.onClick(v); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java index 542a7027a4f..74f112b27fc 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java @@ -1,380 +1,114 @@ package io.sentry.android.core; -import android.app.AlertDialog; import android.content.Context; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; -import io.sentry.IScopes; -import io.sentry.Sentry; import io.sentry.SentryFeedbackOptions; -import io.sentry.SentryIntegrationPackageStorage; -import io.sentry.SentryLevel; -import io.sentry.SentryOptions; -import io.sentry.protocol.Feedback; import io.sentry.protocol.SentryId; -import io.sentry.protocol.User; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class SentryUserFeedbackDialog extends AlertDialog { - - private boolean isCancelable = false; - private @Nullable SentryId currentReplayId; - private final @Nullable SentryId associatedEventId; - private @Nullable OnDismissListener delegate; - - private final @Nullable OptionsConfiguration configuration; - private final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator; +/** + * @deprecated Use {@link SentryUserFeedbackForm} instead. + */ +@Deprecated +public final class SentryUserFeedbackDialog extends SentryUserFeedbackForm { SentryUserFeedbackDialog( final @NotNull Context context, final int themeResId, final @Nullable SentryId associatedEventId, - final @Nullable OptionsConfiguration configuration, + final @Nullable SentryUserFeedbackForm.OptionsConfiguration configuration, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - super(context, themeResId); - this.associatedEventId = associatedEventId; - this.configuration = configuration; - this.configurator = configurator; - SentryIntegrationPackageStorage.getInstance().addIntegration("UserFeedbackWidget"); - } - - @Override - public void setCancelable(boolean cancelable) { - super.setCancelable(cancelable); - isCancelable = cancelable; - } - - @Override - @SuppressWarnings("deprecation") - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.sentry_dialog_user_feedback); - setCancelable(isCancelable); - - final @NotNull SentryFeedbackOptions feedbackOptions = - new SentryFeedbackOptions(Sentry.getCurrentScopes().getOptions().getFeedbackOptions()); - if (configuration != null) { - configuration.configure(getContext(), feedbackOptions); - } - if (configurator != null) { - configurator.configure(feedbackOptions); - } - final @NotNull TextView lblTitle = findViewById(R.id.sentry_dialog_user_feedback_title); - final @NotNull ImageView imgLogo = findViewById(R.id.sentry_dialog_user_feedback_logo); - final @NotNull TextView lblName = findViewById(R.id.sentry_dialog_user_feedback_txt_name); - final @NotNull EditText edtName = findViewById(R.id.sentry_dialog_user_feedback_edt_name); - final @NotNull TextView lblEmail = findViewById(R.id.sentry_dialog_user_feedback_txt_email); - final @NotNull EditText edtEmail = findViewById(R.id.sentry_dialog_user_feedback_edt_email); - final @NotNull TextView lblMessage = - findViewById(R.id.sentry_dialog_user_feedback_txt_description); - final @NotNull EditText edtMessage = - findViewById(R.id.sentry_dialog_user_feedback_edt_description); - final @NotNull Button btnSend = findViewById(R.id.sentry_dialog_user_feedback_btn_send); - final @NotNull Button btnCancel = findViewById(R.id.sentry_dialog_user_feedback_btn_cancel); - - if (feedbackOptions.isShowBranding()) { - imgLogo.setVisibility(View.VISIBLE); - } else { - imgLogo.setVisibility(View.GONE); - } - - // If name is required, ignore showName flag - if (!feedbackOptions.isShowName() && !feedbackOptions.isNameRequired()) { - lblName.setVisibility(View.GONE); - edtName.setVisibility(View.GONE); - } else { - lblName.setVisibility(View.VISIBLE); - edtName.setVisibility(View.VISIBLE); - lblName.setText(feedbackOptions.getNameLabel()); - edtName.setHint(feedbackOptions.getNamePlaceholder()); - if (feedbackOptions.isNameRequired()) { - lblName.append(feedbackOptions.getIsRequiredLabel()); - } - } - - // If email is required, ignore showEmail flag - if (!feedbackOptions.isShowEmail() && !feedbackOptions.isEmailRequired()) { - lblEmail.setVisibility(View.GONE); - edtEmail.setVisibility(View.GONE); - } else { - lblEmail.setVisibility(View.VISIBLE); - edtEmail.setVisibility(View.VISIBLE); - lblEmail.setText(feedbackOptions.getEmailLabel()); - edtEmail.setHint(feedbackOptions.getEmailPlaceholder()); - if (feedbackOptions.isEmailRequired()) { - lblEmail.append(feedbackOptions.getIsRequiredLabel()); - } - } - - // If Sentry user is set, and useSentryUser is true, populate the name and email - if (feedbackOptions.isUseSentryUser()) { - final @Nullable User user = Sentry.getCurrentScopes().getScope().getUser(); - if (user != null) { - edtName.setText(user.getUsername()); - edtEmail.setText(user.getEmail()); - } - } - - lblMessage.setText(feedbackOptions.getMessageLabel()); - lblMessage.append(feedbackOptions.getIsRequiredLabel()); - edtMessage.setHint(feedbackOptions.getMessagePlaceholder()); - lblTitle.setText(feedbackOptions.getFormTitle()); - - btnSend.setText(feedbackOptions.getSubmitButtonLabel()); - btnSend.setOnClickListener( - v -> { - // Gather fields and trim them - final @NotNull String name = edtName.getText().toString().trim(); - final @NotNull String email = edtEmail.getText().toString().trim(); - final @NotNull String message = edtMessage.getText().toString().trim(); - - // If a required field is missing, shows the error label - if (name.isEmpty() && feedbackOptions.isNameRequired()) { - edtName.setError(lblName.getText()); - return; - } - - if (email.isEmpty() && feedbackOptions.isEmailRequired()) { - edtEmail.setError(lblEmail.getText()); - return; - } - - if (message.isEmpty()) { - edtMessage.setError(lblMessage.getText()); - return; - } - - // Create the feedback object - final @NotNull Feedback feedback = new Feedback(message); - feedback.setName(name); - feedback.setContactEmail(email); - if (associatedEventId != null) { - feedback.setAssociatedEventId(associatedEventId); - } - if (currentReplayId != null) { - feedback.setReplayId(currentReplayId); - } - - // Capture the feedback. If the ID is empty, it means that the feedback was not sent - final @NotNull SentryId id = Sentry.captureFeedback(feedback); - if (!id.equals(SentryId.EMPTY_ID)) { - Toast.makeText( - getContext(), feedbackOptions.getSuccessMessageText(), Toast.LENGTH_SHORT) - .show(); - final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitSuccess = - feedbackOptions.getOnSubmitSuccess(); - if (onSubmitSuccess != null) { - onSubmitSuccess.call(feedback); - } - } else { - final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitError = - feedbackOptions.getOnSubmitError(); - if (onSubmitError != null) { - onSubmitError.call(feedback); - } - } - cancel(); - }); - - btnCancel.setText(feedbackOptions.getCancelButtonLabel()); - btnCancel.setOnClickListener(v -> cancel()); - setOnDismissListener(delegate); - } - - @Override - public void setOnDismissListener(final @Nullable OnDismissListener listener) { - delegate = listener; - // If the user set a custom onDismissListener, we ensure it doesn't override the onFormClose - final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); - final @Nullable Runnable onFormClose = options.getFeedbackOptions().getOnFormClose(); - if (onFormClose != null) { - super.setOnDismissListener( - dialog -> { - onFormClose.run(); - currentReplayId = null; - if (delegate != null) { - delegate.onDismiss(dialog); - } - }); - } else { - super.setOnDismissListener(delegate); - } - } - - @Override - protected void onStart() { - super.onStart(); - final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); - final @NotNull SentryFeedbackOptions feedbackOptions = options.getFeedbackOptions(); - final @Nullable Runnable onFormOpen = feedbackOptions.getOnFormOpen(); - if (onFormOpen != null) { - onFormOpen.run(); - } - options.getReplayController().captureReplay(false); - currentReplayId = options.getReplayController().getReplayId(); - } - - @Override - public void show() { - // If Sentry is disabled, don't show the dialog, but log a warning - final @NotNull IScopes scopes = Sentry.getCurrentScopes(); - final @NotNull SentryOptions options = scopes.getOptions(); - if (!scopes.isEnabled() || !options.isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Sentry is disabled. Feedback dialog won't be shown."); - return; - } - // Otherwise, show the dialog - super.show(); + super(context, themeResId, associatedEventId, configuration, configurator); } - public static class Builder { - - @Nullable OptionsConfiguration configuration; - @Nullable SentryFeedbackOptions.OptionsConfigurator configurator; - @Nullable SentryId associatedEventId; - final @NotNull Context context; - final int themeResId; + /** + * @deprecated Use {@link SentryUserFeedbackForm.Builder} instead. + */ + @Deprecated + public static class Builder extends SentryUserFeedbackForm.Builder { /** * Creates a builder for a {@link SentryUserFeedbackDialog} that uses the default alert dialog * theme. * - *

The default alert dialog theme is defined by {@link android.R.attr#alertDialogTheme} - * within the parent {@code context}'s theme. - * * @param context the parent context + * @deprecated Use {@link SentryUserFeedbackForm.Builder#Builder(Context)} instead. */ + @Deprecated public Builder(final @NotNull Context context) { - this(context, 0); + super(context); } /** * Creates a builder for a {@link SentryUserFeedbackDialog} that uses an explicit theme * resource. * - *

The specified theme resource ({@code themeResId}) is applied on top of the parent {@code - * context}'s theme. It may be specified as a style resource containing a fully-populated theme, - * such as {@link android.R.style#Theme_Material_Dialog}, to replace all attributes in the - * parent {@code context}'s theme including primary and accent colors. - * - *

To preserve attributes such as primary and accent colors, the {@code themeResId} may - * instead be specified as an overlay theme such as {@link - * android.R.style#ThemeOverlay_Material_Dialog}. This will override only the window attributes - * necessary to style the alert window as a dialog. - * - *

Alternatively, the {@code themeResId} may be specified as {@code 0} to use the parent - * {@code context}'s resolved value for {@link android.R.attr#alertDialogTheme}. - * * @param context the parent context - * @param themeResId the resource ID of the theme against which to inflate this dialog, or - * {@code 0} to use the parent {@code context}'s default alert dialog theme + * @param themeResId the resource ID of the theme + * @deprecated Use {@link SentryUserFeedbackForm.Builder#Builder(Context, int)} instead. */ + @Deprecated public Builder(Context context, int themeResId) { - this(context, themeResId, null); + super(context, themeResId); } /** - * Creates a builder for a {@link SentryUserFeedbackDialog} that uses the default alert dialog - * theme. The {@code configuration} can be used to configure the feedback options for this - * specific dialog. - * - *

The default alert dialog theme is defined by {@link android.R.attr#alertDialogTheme} - * within the parent {@code context}'s theme. + * Creates a builder for a {@link SentryUserFeedbackDialog} with a configuration. * * @param context the parent context - * @param configuration the configuration for the feedback options, can be {@code null} to use - * the global feedback options. + * @param configuration the configuration for the feedback options + * @deprecated Use {@link SentryUserFeedbackForm.Builder#Builder(Context, + * SentryUserFeedbackForm.OptionsConfiguration)} instead. */ + @Deprecated public Builder( - final @NotNull Context context, final @Nullable OptionsConfiguration configuration) { - this(context, 0, configuration); + final @NotNull Context context, + final @Nullable SentryUserFeedbackForm.OptionsConfiguration configuration) { + super(context, configuration); } /** - * Creates a builder for a {@link SentryUserFeedbackDialog} that uses an explicit theme - * resource. The {@code configuration} can be used to configure the feedback options for this - * specific dialog. - * - *

The specified theme resource ({@code themeResId}) is applied on top of the parent {@code - * context}'s theme. It may be specified as a style resource containing a fully-populated theme, - * such as {@link android.R.style#Theme_Material_Dialog}, to replace all attributes in the - * parent {@code context}'s theme including primary and accent colors. - * - *

To preserve attributes such as primary and accent colors, the {@code themeResId} may - * instead be specified as an overlay theme such as {@link - * android.R.style#ThemeOverlay_Material_Dialog}. This will override only the window attributes - * necessary to style the alert window as a dialog. - * - *

Alternatively, the {@code themeResId} may be specified as {@code 0} to use the parent - * {@code context}'s resolved value for {@link android.R.attr#alertDialogTheme}. + * Creates a builder for a {@link SentryUserFeedbackDialog} with a theme and configuration. * * @param context the parent context - * @param themeResId the resource ID of the theme against which to inflate this dialog, or - * {@code 0} to use the parent {@code context}'s default alert dialog theme - * @param configuration the configuration for the feedback options, can be {@code null} to use - * the global feedback options. + * @param themeResId the resource ID of the theme + * @param configuration the configuration for the feedback options + * @deprecated Use {@link SentryUserFeedbackForm.Builder#Builder(Context, int, + * SentryUserFeedbackForm.OptionsConfiguration)} instead. */ + @Deprecated public Builder( final @NotNull Context context, final int themeResId, - final @Nullable OptionsConfiguration configuration) { - this.context = context; - this.themeResId = themeResId; - this.configuration = configuration; + final @Nullable SentryUserFeedbackForm.OptionsConfiguration configuration) { + super(context, themeResId, configuration); } - /** - * Sets the configuration for the feedback options. - * - * @param configurator the configuration for the feedback options, can be {@code null} to use - * the global feedback options. - */ + @Deprecated + @Override public Builder configurator( final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - this.configurator = configurator; + super.configurator(configurator); return this; } - /** - * Sets the associated event ID for the feedback. - * - * @param associatedEventId the associated event ID for the feedback, can be {@code null} to - * avoid associating the feedback to an event. - */ + @Deprecated + @Override public Builder associatedEventId(final @Nullable SentryId associatedEventId) { - this.associatedEventId = associatedEventId; + super.associatedEventId(associatedEventId); return this; } - /** - * Builds a new {@link SentryUserFeedbackDialog} with the specified context, theme, and - * configuration. - * - * @return a new instance of {@link SentryUserFeedbackDialog} - */ + @Override public SentryUserFeedbackDialog create() { return new SentryUserFeedbackDialog( context, themeResId, associatedEventId, configuration, configurator); } } - /** Configuration callback for feedback options. */ - public interface OptionsConfiguration { - - /** - * configure the feedback options - * - * @param context the context of the feedback dialog - * @param options the feedback options - */ - void configure(final @NotNull Context context, final @NotNull SentryFeedbackOptions options); - } + /** + * @deprecated Use {@link SentryUserFeedbackForm.OptionsConfiguration} instead. + */ + @Deprecated + public interface OptionsConfiguration extends SentryUserFeedbackForm.OptionsConfiguration {} } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java new file mode 100644 index 00000000000..2ad1511175f --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java @@ -0,0 +1,379 @@ +package io.sentry.android.core; + +import android.app.AlertDialog; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; +import io.sentry.IScopes; +import io.sentry.Sentry; +import io.sentry.SentryFeedbackOptions; +import io.sentry.SentryIntegrationPackageStorage; +import io.sentry.SentryLevel; +import io.sentry.SentryOptions; +import io.sentry.protocol.Feedback; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.User; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SentryUserFeedbackForm extends AlertDialog { + + private boolean isCancelable = false; + private @Nullable SentryId currentReplayId; + private final @Nullable SentryId associatedEventId; + private @Nullable OnDismissListener delegate; + + private final @Nullable OptionsConfiguration configuration; + private final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator; + + SentryUserFeedbackForm( + final @NotNull Context context, + final int themeResId, + final @Nullable SentryId associatedEventId, + final @Nullable OptionsConfiguration configuration, + final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { + super(context, themeResId); + this.associatedEventId = associatedEventId; + this.configuration = configuration; + this.configurator = configurator; + SentryIntegrationPackageStorage.getInstance().addIntegration("UserFeedbackWidget"); + } + + @Override + public void setCancelable(boolean cancelable) { + super.setCancelable(cancelable); + isCancelable = cancelable; + } + + @Override + @SuppressWarnings("deprecation") + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sentry_dialog_user_feedback); + setCancelable(isCancelable); + + final @NotNull SentryFeedbackOptions feedbackOptions = + new SentryFeedbackOptions(Sentry.getCurrentScopes().getOptions().getFeedbackOptions()); + if (configuration != null) { + configuration.configure(getContext(), feedbackOptions); + } + if (configurator != null) { + configurator.configure(feedbackOptions); + } + final @NotNull TextView lblTitle = findViewById(R.id.sentry_dialog_user_feedback_title); + final @NotNull ImageView imgLogo = findViewById(R.id.sentry_dialog_user_feedback_logo); + final @NotNull TextView lblName = findViewById(R.id.sentry_dialog_user_feedback_txt_name); + final @NotNull EditText edtName = findViewById(R.id.sentry_dialog_user_feedback_edt_name); + final @NotNull TextView lblEmail = findViewById(R.id.sentry_dialog_user_feedback_txt_email); + final @NotNull EditText edtEmail = findViewById(R.id.sentry_dialog_user_feedback_edt_email); + final @NotNull TextView lblMessage = + findViewById(R.id.sentry_dialog_user_feedback_txt_description); + final @NotNull EditText edtMessage = + findViewById(R.id.sentry_dialog_user_feedback_edt_description); + final @NotNull Button btnSend = findViewById(R.id.sentry_dialog_user_feedback_btn_send); + final @NotNull Button btnCancel = findViewById(R.id.sentry_dialog_user_feedback_btn_cancel); + + if (feedbackOptions.isShowBranding()) { + imgLogo.setVisibility(View.VISIBLE); + } else { + imgLogo.setVisibility(View.GONE); + } + + // If name is required, ignore showName flag + if (!feedbackOptions.isShowName() && !feedbackOptions.isNameRequired()) { + lblName.setVisibility(View.GONE); + edtName.setVisibility(View.GONE); + } else { + lblName.setVisibility(View.VISIBLE); + edtName.setVisibility(View.VISIBLE); + lblName.setText(feedbackOptions.getNameLabel()); + edtName.setHint(feedbackOptions.getNamePlaceholder()); + if (feedbackOptions.isNameRequired()) { + lblName.append(feedbackOptions.getIsRequiredLabel()); + } + } + + // If email is required, ignore showEmail flag + if (!feedbackOptions.isShowEmail() && !feedbackOptions.isEmailRequired()) { + lblEmail.setVisibility(View.GONE); + edtEmail.setVisibility(View.GONE); + } else { + lblEmail.setVisibility(View.VISIBLE); + edtEmail.setVisibility(View.VISIBLE); + lblEmail.setText(feedbackOptions.getEmailLabel()); + edtEmail.setHint(feedbackOptions.getEmailPlaceholder()); + if (feedbackOptions.isEmailRequired()) { + lblEmail.append(feedbackOptions.getIsRequiredLabel()); + } + } + + // If Sentry user is set, and useSentryUser is true, populate the name and email + if (feedbackOptions.isUseSentryUser()) { + final @Nullable User user = Sentry.getCurrentScopes().getScope().getUser(); + if (user != null) { + edtName.setText(user.getUsername()); + edtEmail.setText(user.getEmail()); + } + } + + lblMessage.setText(feedbackOptions.getMessageLabel()); + lblMessage.append(feedbackOptions.getIsRequiredLabel()); + edtMessage.setHint(feedbackOptions.getMessagePlaceholder()); + lblTitle.setText(feedbackOptions.getFormTitle()); + + btnSend.setText(feedbackOptions.getSubmitButtonLabel()); + btnSend.setOnClickListener( + v -> { + // Gather fields and trim them + final @NotNull String name = edtName.getText().toString().trim(); + final @NotNull String email = edtEmail.getText().toString().trim(); + final @NotNull String message = edtMessage.getText().toString().trim(); + + // If a required field is missing, shows the error label + if (name.isEmpty() && feedbackOptions.isNameRequired()) { + edtName.setError(lblName.getText()); + return; + } + + if (email.isEmpty() && feedbackOptions.isEmailRequired()) { + edtEmail.setError(lblEmail.getText()); + return; + } + + if (message.isEmpty()) { + edtMessage.setError(lblMessage.getText()); + return; + } + + // Create the feedback object + final @NotNull Feedback feedback = new Feedback(message); + feedback.setName(name); + feedback.setContactEmail(email); + if (associatedEventId != null) { + feedback.setAssociatedEventId(associatedEventId); + } + if (currentReplayId != null) { + feedback.setReplayId(currentReplayId); + } + + // Capture the feedback. If the ID is empty, it means that the feedback was not sent + final @NotNull SentryId id = Sentry.captureFeedback(feedback); + if (!id.equals(SentryId.EMPTY_ID)) { + Toast.makeText( + getContext(), feedbackOptions.getSuccessMessageText(), Toast.LENGTH_SHORT) + .show(); + final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitSuccess = + feedbackOptions.getOnSubmitSuccess(); + if (onSubmitSuccess != null) { + onSubmitSuccess.call(feedback); + } + } else { + final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitError = + feedbackOptions.getOnSubmitError(); + if (onSubmitError != null) { + onSubmitError.call(feedback); + } + } + cancel(); + }); + + btnCancel.setText(feedbackOptions.getCancelButtonLabel()); + btnCancel.setOnClickListener(v -> cancel()); + setOnDismissListener(delegate); + } + + @Override + public void setOnDismissListener(final @Nullable OnDismissListener listener) { + delegate = listener; + // If the user set a custom onDismissListener, we ensure it doesn't override the onFormClose + final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); + final @Nullable Runnable onFormClose = options.getFeedbackOptions().getOnFormClose(); + if (onFormClose != null) { + super.setOnDismissListener( + dialog -> { + onFormClose.run(); + currentReplayId = null; + if (delegate != null) { + delegate.onDismiss(dialog); + } + }); + } else { + super.setOnDismissListener(delegate); + } + } + + @Override + protected void onStart() { + super.onStart(); + final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); + final @NotNull SentryFeedbackOptions feedbackOptions = options.getFeedbackOptions(); + final @Nullable Runnable onFormOpen = feedbackOptions.getOnFormOpen(); + if (onFormOpen != null) { + onFormOpen.run(); + } + options.getReplayController().captureReplay(false); + currentReplayId = options.getReplayController().getReplayId(); + } + + @Override + public void show() { + // If Sentry is disabled, don't show the dialog, but log a warning + final @NotNull IScopes scopes = Sentry.getCurrentScopes(); + final @NotNull SentryOptions options = scopes.getOptions(); + if (!scopes.isEnabled() || !options.isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Sentry is disabled. Feedback dialog won't be shown."); + return; + } + // Otherwise, show the dialog + super.show(); + } + + public static class Builder { + + @Nullable OptionsConfiguration configuration; + @Nullable SentryFeedbackOptions.OptionsConfigurator configurator; + @Nullable SentryId associatedEventId; + final @NotNull Context context; + final int themeResId; + + /** + * Creates a builder for a {@link SentryUserFeedbackForm} that uses the default alert dialog + * theme. + * + *

The default alert dialog theme is defined by {@link android.R.attr#alertDialogTheme} + * within the parent {@code context}'s theme. + * + * @param context the parent context + */ + public Builder(final @NotNull Context context) { + this(context, 0); + } + + /** + * Creates a builder for a {@link SentryUserFeedbackForm} that uses an explicit theme resource. + * + *

The specified theme resource ({@code themeResId}) is applied on top of the parent {@code + * context}'s theme. It may be specified as a style resource containing a fully-populated theme, + * such as {@link android.R.style#Theme_Material_Dialog}, to replace all attributes in the + * parent {@code context}'s theme including primary and accent colors. + * + *

To preserve attributes such as primary and accent colors, the {@code themeResId} may + * instead be specified as an overlay theme such as {@link + * android.R.style#ThemeOverlay_Material_Dialog}. This will override only the window attributes + * necessary to style the alert window as a dialog. + * + *

Alternatively, the {@code themeResId} may be specified as {@code 0} to use the parent + * {@code context}'s resolved value for {@link android.R.attr#alertDialogTheme}. + * + * @param context the parent context + * @param themeResId the resource ID of the theme against which to inflate this dialog, or + * {@code 0} to use the parent {@code context}'s default alert dialog theme + */ + public Builder(Context context, int themeResId) { + this(context, themeResId, null); + } + + /** + * Creates a builder for a {@link SentryUserFeedbackForm} that uses the default alert dialog + * theme. The {@code configuration} can be used to configure the feedback options for this + * specific dialog. + * + *

The default alert dialog theme is defined by {@link android.R.attr#alertDialogTheme} + * within the parent {@code context}'s theme. + * + * @param context the parent context + * @param configuration the configuration for the feedback options, can be {@code null} to use + * the global feedback options. + */ + public Builder( + final @NotNull Context context, final @Nullable OptionsConfiguration configuration) { + this(context, 0, configuration); + } + + /** + * Creates a builder for a {@link SentryUserFeedbackForm} that uses an explicit theme resource. + * The {@code configuration} can be used to configure the feedback options for this specific + * dialog. + * + *

The specified theme resource ({@code themeResId}) is applied on top of the parent {@code + * context}'s theme. It may be specified as a style resource containing a fully-populated theme, + * such as {@link android.R.style#Theme_Material_Dialog}, to replace all attributes in the + * parent {@code context}'s theme including primary and accent colors. + * + *

To preserve attributes such as primary and accent colors, the {@code themeResId} may + * instead be specified as an overlay theme such as {@link + * android.R.style#ThemeOverlay_Material_Dialog}. This will override only the window attributes + * necessary to style the alert window as a dialog. + * + *

Alternatively, the {@code themeResId} may be specified as {@code 0} to use the parent + * {@code context}'s resolved value for {@link android.R.attr#alertDialogTheme}. + * + * @param context the parent context + * @param themeResId the resource ID of the theme against which to inflate this dialog, or + * {@code 0} to use the parent {@code context}'s default alert dialog theme + * @param configuration the configuration for the feedback options, can be {@code null} to use + * the global feedback options. + */ + public Builder( + final @NotNull Context context, + final int themeResId, + final @Nullable OptionsConfiguration configuration) { + this.context = context; + this.themeResId = themeResId; + this.configuration = configuration; + } + + /** + * Sets the configuration for the feedback options. + * + * @param configurator the configuration for the feedback options, can be {@code null} to use + * the global feedback options. + */ + public Builder configurator( + final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { + this.configurator = configurator; + return this; + } + + /** + * Sets the associated event ID for the feedback. + * + * @param associatedEventId the associated event ID for the feedback, can be {@code null} to + * avoid associating the feedback to an event. + */ + public Builder associatedEventId(final @Nullable SentryId associatedEventId) { + this.associatedEventId = associatedEventId; + return this; + } + + /** + * Builds a new {@link SentryUserFeedbackForm} with the specified context, theme, and + * configuration. + * + * @return a new instance of {@link SentryUserFeedbackForm} + */ + public SentryUserFeedbackForm create() { + return new SentryUserFeedbackForm( + context, themeResId, associatedEventId, configuration, configurator); + } + } + + /** Configuration callback for feedback options. */ + public interface OptionsConfiguration { + + /** + * configure the feedback options + * + * @param context the context of the feedback dialog + * @param options the feedback options + */ + void configure(final @NotNull Context context, final @NotNull SentryFeedbackOptions options); + } +} diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index be54bf7768b..f8724d286f8 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -18,7 +18,7 @@ import io.sentry.MainEventProcessor import io.sentry.NoOpContinuousProfiler import io.sentry.NoOpTransactionProfiler import io.sentry.SentryOptions -import io.sentry.android.core.SentryAndroidOptions.AndroidUserFeedbackIDialogHandler +import io.sentry.android.core.SentryAndroidOptions.AndroidUserFeedbackFormHandler import io.sentry.android.core.cache.AndroidEnvelopeCache import io.sentry.android.core.internal.debugmeta.AssetsDebugMetaLoader import io.sentry.android.core.internal.gestures.AndroidViewGestureTargetLocator @@ -882,9 +882,9 @@ class AndroidOptionsInitializerTest { } @Test - fun `AndroidUserFeedbackIDialogHandler is set as feedback dialog handler`() { + fun `AndroidUserFeedbackFormHandler is set as feedback form handler`() { fixture.initSut() - assertIs(fixture.sentryOptions.feedbackOptions.dialogHandler) + assertIs(fixture.sentryOptions.feedbackOptions.formHandler) } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/FeedbackShakeIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/FeedbackShakeIntegrationTest.kt index cb940686c30..bddc9395c0d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/FeedbackShakeIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/FeedbackShakeIntegrationTest.kt @@ -22,10 +22,10 @@ class FeedbackShakeIntegrationTest { val scopes = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } val activity = mock() - val dialogHandler = mock() + val formHandler = mock() init { - options.feedbackOptions.setDialogHandler(dialogHandler) + options.feedbackOptions.setFormHandler(formHandler) } fun getSut(useShakeGesture: Boolean = true): FeedbackShakeIntegration { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackDialogTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt similarity index 94% rename from sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackDialogTest.kt rename to sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt index bd60859c608..04f6a35716b 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackDialogTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt @@ -26,7 +26,7 @@ import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) -class SentryUserFeedbackDialogTest { +class SentryUserFeedbackFormTest { class Fixture { val application: Context = ApplicationProvider.getApplicationContext() private val mockDsn = "http://key@localhost/proj" @@ -55,10 +55,10 @@ class SentryUserFeedbackDialogTest { fun getSut( associatedEventId: SentryId? = null, - configuration: SentryUserFeedbackDialog.OptionsConfiguration? = null, + configuration: SentryUserFeedbackForm.OptionsConfiguration? = null, configurator: SentryFeedbackOptions.OptionsConfigurator? = null, - ): SentryUserFeedbackDialog = - SentryUserFeedbackDialog(application, 0, associatedEventId, configuration, configurator) + ): SentryUserFeedbackForm = + SentryUserFeedbackForm(application, 0, associatedEventId, configuration, configurator) } private val fixture = Fixture() diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt index 39dfae40203..4942abb778d 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt @@ -28,7 +28,7 @@ import io.sentry.SentryOptions import io.sentry.android.core.AndroidLogger import io.sentry.android.core.R import io.sentry.android.core.SentryUserFeedbackButton -import io.sentry.android.core.SentryUserFeedbackDialog +import io.sentry.android.core.SentryUserFeedbackForm import io.sentry.assertEnvelopeFeedback import io.sentry.protocol.SentryId import io.sentry.protocol.User @@ -49,21 +49,21 @@ class UserFeedbackUiTest : BaseUiTest() { @Test fun userFeedbackNotShownWhenSdkDisabled() { launchActivity().onActivity { - SentryUserFeedbackDialog.Builder(it).create().show() + SentryUserFeedbackForm.Builder(it).create().show() } onView(withId(R.id.sentry_dialog_user_feedback_layout)).check(doesNotExist()) } @Test fun userFeedbackNotShownWhenSdkDisabledViaApi() { - launchActivity().onActivity { Sentry.showUserFeedbackDialog() } + launchActivity().onActivity { Sentry.showUserFeedbackForm() } onView(withId(R.id.sentry_dialog_user_feedback_layout)).check(doesNotExist()) } @Test fun userFeedbackShownViaApi() { initSentry() - launchActivity().onActivity { Sentry.showUserFeedbackDialog() } + launchActivity().onActivity { Sentry.showUserFeedbackForm() } onView(withId(R.id.sentry_dialog_user_feedback_layout)) .inRoot(isDialog()) @@ -639,12 +639,12 @@ class UserFeedbackUiTest : BaseUiTest() { private fun showDialogAndCheck( associatedEventId: SentryId? = null, - checker: (dialog: SentryUserFeedbackDialog) -> Unit = {}, + checker: (dialog: SentryUserFeedbackForm) -> Unit = {}, ) { - lateinit var dialog: SentryUserFeedbackDialog + lateinit var dialog: SentryUserFeedbackForm val feedbackScenario = launchActivity() feedbackScenario.onActivity { - dialog = SentryUserFeedbackDialog.Builder(it).associatedEventId(associatedEventId).create() + dialog = SentryUserFeedbackForm.Builder(it).associatedEventId(associatedEventId).create() dialog.show() } diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt index 93460b88893..8ded14f0765 100644 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt @@ -21,7 +21,7 @@ public fun SentryUserFeedbackButton( text: String = "Report a Bug", configurator: SentryFeedbackOptions.OptionsConfigurator? = null, ) { - Button(modifier = modifier, onClick = { Sentry.showUserFeedbackDialog(configurator) }) { + Button(modifier = modifier, onClick = { Sentry.showUserFeedbackForm(configurator) }) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index b9cbb2ae1b2..967d19d7882 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2766,6 +2766,9 @@ public final class io/sentry/Sentry { public static fun showUserFeedbackDialog ()V public static fun showUserFeedbackDialog (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V public static fun showUserFeedbackDialog (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public static fun showUserFeedbackForm ()V + public static fun showUserFeedbackForm (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public static fun showUserFeedbackForm (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V public static fun startProfiler ()V public static fun startSession ()V public static fun startTransaction (Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; @@ -3151,12 +3154,12 @@ public final class io/sentry/SentryExecutorService : io/sentry/ISentryExecutorSe } public final class io/sentry/SentryFeedbackOptions { - public fun (Lio/sentry/SentryFeedbackOptions$IDialogHandler;)V + public fun (Lio/sentry/SentryFeedbackOptions$IFormHandler;)V public fun (Lio/sentry/SentryFeedbackOptions;)V public fun getCancelButtonLabel ()Ljava/lang/CharSequence; - public fun getDialogHandler ()Lio/sentry/SentryFeedbackOptions$IDialogHandler; public fun getEmailLabel ()Ljava/lang/CharSequence; public fun getEmailPlaceholder ()Ljava/lang/CharSequence; + public fun getFormHandler ()Lio/sentry/SentryFeedbackOptions$IFormHandler; public fun getFormTitle ()Ljava/lang/CharSequence; public fun getIsRequiredLabel ()Ljava/lang/CharSequence; public fun getMessageLabel ()Ljava/lang/CharSequence; @@ -3177,10 +3180,10 @@ public final class io/sentry/SentryFeedbackOptions { public fun isUseSentryUser ()Z public fun isUseShakeGesture ()Z public fun setCancelButtonLabel (Ljava/lang/CharSequence;)V - public fun setDialogHandler (Lio/sentry/SentryFeedbackOptions$IDialogHandler;)V public fun setEmailLabel (Ljava/lang/CharSequence;)V public fun setEmailPlaceholder (Ljava/lang/CharSequence;)V public fun setEmailRequired (Z)V + public fun setFormHandler (Lio/sentry/SentryFeedbackOptions$IFormHandler;)V public fun setFormTitle (Ljava/lang/CharSequence;)V public fun setIsRequiredLabel (Ljava/lang/CharSequence;)V public fun setMessageLabel (Ljava/lang/CharSequence;)V @@ -3202,8 +3205,8 @@ public final class io/sentry/SentryFeedbackOptions { public fun toString ()Ljava/lang/String; } -public abstract interface class io/sentry/SentryFeedbackOptions$IDialogHandler { - public abstract fun showDialog (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V +public abstract interface class io/sentry/SentryFeedbackOptions$IFormHandler { + public abstract fun showForm (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V } public abstract interface class io/sentry/SentryFeedbackOptions$OptionsConfigurator { diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index fee19dc4d09..5d4b2adba56 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -1355,20 +1355,52 @@ public static IMetricsApi metrics() { return getCurrentScopes().metrics(); } + public static void showUserFeedbackForm() { + showUserFeedbackForm(null); + } + + public static void showUserFeedbackForm( + final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { + showUserFeedbackForm(null, configurator); + } + + public static void showUserFeedbackForm( + final @Nullable SentryId associatedEventId, + final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { + final @NotNull SentryOptions options = getCurrentScopes().getOptions(); + options.getFeedbackOptions().getFormHandler().showForm(associatedEventId, configurator); + } + + /** + * @deprecated Use {@link #showUserFeedbackForm()} instead. + */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog() { - showUserFeedbackDialog(null); + showUserFeedbackForm(); } + /** + * @deprecated Use {@link #showUserFeedbackForm(SentryFeedbackOptions.OptionsConfigurator)} + * instead. + */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog( final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - showUserFeedbackDialog(null, configurator); + showUserFeedbackForm(configurator); } + /** + * @deprecated Use {@link #showUserFeedbackForm(SentryId, + * SentryFeedbackOptions.OptionsConfigurator)} instead. + */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - final @NotNull SentryOptions options = getCurrentScopes().getOptions(); - options.getFeedbackOptions().getDialogHandler().showDialog(associatedEventId, configurator); + showUserFeedbackForm(associatedEventId, configurator); } /** diff --git a/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java b/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java index 2a0ead54234..fcaa919d725 100644 --- a/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java +++ b/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java @@ -91,10 +91,10 @@ public final class SentryFeedbackOptions { /** Callback called when there is an error submitting feedback via the prepared form. */ private @Nullable SentryFeedbackCallback onSubmitError; - private @NotNull IDialogHandler iDialogHandler; + private @NotNull IFormHandler iFormHandler; - public SentryFeedbackOptions(@NotNull IDialogHandler iDialogHandler) { - this.iDialogHandler = iDialogHandler; + public SentryFeedbackOptions(@NotNull IFormHandler iFormHandler) { + this.iFormHandler = iFormHandler; } /** Creates a copy of the passed {@link SentryFeedbackOptions}. */ @@ -121,7 +121,7 @@ public SentryFeedbackOptions(final @NotNull SentryFeedbackOptions other) { this.onFormClose = other.onFormClose; this.onSubmitSuccess = other.onSubmitSuccess; this.onSubmitError = other.onSubmitError; - this.iDialogHandler = other.iDialogHandler; + this.iFormHandler = other.iFormHandler; } /** @@ -535,23 +535,23 @@ public void setOnSubmitError(final @Nullable SentryFeedbackCallback onSubmitErro } /** - * Sets the dialog handler to be used to show the feedback form. + * Sets the form handler to be used to show the feedback form. * - * @param iDialogHandler the dialog handler to be used to show the feedback form + * @param iFormHandler the form handler to be used to show the feedback form */ @ApiStatus.Internal - public void setDialogHandler(final @NotNull IDialogHandler iDialogHandler) { - this.iDialogHandler = iDialogHandler; + public void setFormHandler(final @NotNull IFormHandler iFormHandler) { + this.iFormHandler = iFormHandler; } /** - * Gets the dialog handler to be used to show the feedback form. + * Gets the form handler to be used to show the feedback form. * - * @return the dialog handler to be used to show the feedback form + * @return the form handler to be used to show the feedback form */ @ApiStatus.Internal - public @NotNull IDialogHandler getDialogHandler() { - return iDialogHandler; + public @NotNull IFormHandler getFormHandler() { + return iFormHandler; } @Override @@ -609,8 +609,8 @@ public interface SentryFeedbackCallback { } @ApiStatus.Internal - public interface IDialogHandler { - void showDialog( + public interface IFormHandler { + void showForm( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator); } diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 86086f8816b..a6f78cfad9c 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -3397,7 +3397,7 @@ private SentryOptions(final boolean empty) { feedbackOptions = new SentryFeedbackOptions( (associatedEventId, configurator) -> - logger.log(SentryLevel.WARNING, "showDialog() can only be called in Android.")); + logger.log(SentryLevel.WARNING, "showForm() can only be called in Android.")); if (!empty) { setSpanFactory(SpanFactoryFactory.create(new LoadClass(), NoOpLogger.getInstance())); diff --git a/sentry/src/test/java/io/sentry/SentryFeedbackOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryFeedbackOptionsTest.kt index a50aff02dc5..e4b96cb17d0 100644 --- a/sentry/src/test/java/io/sentry/SentryFeedbackOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryFeedbackOptionsTest.kt @@ -1,6 +1,6 @@ package io.sentry -import io.sentry.SentryFeedbackOptions.IDialogHandler +import io.sentry.SentryFeedbackOptions.IFormHandler import kotlin.test.Test import kotlin.test.assertEquals import org.mockito.kotlin.mock @@ -8,7 +8,7 @@ import org.mockito.kotlin.mock class SentryFeedbackOptionsTest { @Test fun `feedback options is initialized with default values`() { - val options = SentryFeedbackOptions(mock()) + val options = SentryFeedbackOptions(mock()) assertEquals(false, options.isNameRequired) assertEquals(true, options.isShowName) assertEquals(false, options.isEmailRequired) @@ -35,7 +35,7 @@ class SentryFeedbackOptionsTest { @Test fun `feedback options copy constructor`() { val options = - SentryFeedbackOptions(mock()).apply { + SentryFeedbackOptions(mock()).apply { isNameRequired = true isShowName = false isEmailRequired = true @@ -80,6 +80,6 @@ class SentryFeedbackOptionsTest { assertEquals(options.onFormClose, optionsCopy.onFormClose) assertEquals(options.onSubmitSuccess, optionsCopy.onSubmitSuccess) assertEquals(options.onSubmitError, optionsCopy.onSubmitError) - assertEquals(options.dialogHandler, optionsCopy.dialogHandler) + assertEquals(options.formHandler, optionsCopy.formHandler) } } diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index da014b30f74..e08d0ed8f72 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -907,8 +907,8 @@ class SentryOptionsTest { setLogger(logger) isDebug = true } - options.feedbackOptions.dialogHandler.showDialog(mock(), mock()) - verify(logger).log(eq(SentryLevel.WARNING), eq("showDialog() can only be called in Android.")) + options.feedbackOptions.formHandler.showForm(mock(), mock()) + verify(logger).log(eq(SentryLevel.WARNING), eq("showForm() can only be called in Android.")) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 25f45816b74..2503b8c755c 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -1,6 +1,6 @@ package io.sentry -import io.sentry.SentryFeedbackOptions.IDialogHandler +import io.sentry.SentryFeedbackOptions.IFormHandler import io.sentry.SentryOptions.ProfilesSamplerCallback import io.sentry.SentryOptions.TracesSamplerCallback import io.sentry.backpressure.BackpressureMonitor @@ -1504,39 +1504,39 @@ class SentryTest { } @Test - fun `showUserFeedbackDialog forwards to feedbackOptions_dialogHandler`() { - val mockDialogHandler = mock() + fun `showUserFeedbackForm forwards to feedbackOptions_formHandler`() { + val mockFormHandler = mock() initForTest { it.dsn = dsn - it.feedbackOptions.dialogHandler = mockDialogHandler + it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.showUserFeedbackDialog() - verify(mockDialogHandler).showDialog(eq(null), eq(null)) + Sentry.showUserFeedbackForm() + verify(mockFormHandler).showForm(eq(null), eq(null)) } @Test - fun `showUserFeedbackDialog forwards to feedbackOptions_dialogHandler with configurator`() { - val mockDialogHandler = mock() + fun `showUserFeedbackForm forwards to feedbackOptions_formHandler with configurator`() { + val mockFormHandler = mock() val configurator = mock() initForTest { it.dsn = dsn - it.feedbackOptions.dialogHandler = mockDialogHandler + it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.showUserFeedbackDialog(configurator) - verify(mockDialogHandler).showDialog(eq(null), eq(configurator)) + Sentry.showUserFeedbackForm(configurator) + verify(mockFormHandler).showForm(eq(null), eq(configurator)) } @Test - fun `showUserFeedbackDialog forwards to feedbackOptions_dialogHandler with associatedEventId and configurator`() { - val mockDialogHandler = mock() + fun `showUserFeedbackForm forwards to feedbackOptions_formHandler with associatedEventId and configurator`() { + val mockFormHandler = mock() val configurator = mock() val associatedEventId = SentryId() initForTest { it.dsn = dsn - it.feedbackOptions.dialogHandler = mockDialogHandler + it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.showUserFeedbackDialog(associatedEventId, configurator) - verify(mockDialogHandler).showDialog(eq(associatedEventId), eq(configurator)) + Sentry.showUserFeedbackForm(associatedEventId, configurator) + verify(mockFormHandler).showForm(eq(associatedEventId), eq(configurator)) } @Test From 9b9966805388bf28bf9e01240f0605689f03ec2b Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 29 Apr 2026 17:01:33 +0200 Subject: [PATCH 02/18] fix(feedback): Preserve binary compatibility for deprecated Builder constructors Use SentryUserFeedbackDialog.OptionsConfiguration as the parameter type in the deprecated Builder constructors so old compiled code looking for the original descriptor still resolves correctly. Co-Authored-By: Claude Opus 4.6 --- sentry-android-core/api/sentry-android-core.api | 4 ++-- .../io/sentry/android/core/SentryUserFeedbackDialog.java | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 61d45783864..4623d6e1316 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -502,8 +502,8 @@ public final class io/sentry/android/core/SentryUserFeedbackDialog : io/sentry/a public class io/sentry/android/core/SentryUserFeedbackDialog$Builder : io/sentry/android/core/SentryUserFeedbackForm$Builder { public fun (Landroid/content/Context;)V public fun (Landroid/content/Context;I)V - public fun (Landroid/content/Context;ILio/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration;)V - public fun (Landroid/content/Context;Lio/sentry/android/core/SentryUserFeedbackForm$OptionsConfiguration;)V + public fun (Landroid/content/Context;ILio/sentry/android/core/SentryUserFeedbackDialog$OptionsConfiguration;)V + public fun (Landroid/content/Context;Lio/sentry/android/core/SentryUserFeedbackDialog$OptionsConfiguration;)V public fun associatedEventId (Lio/sentry/protocol/SentryId;)Lio/sentry/android/core/SentryUserFeedbackDialog$Builder; public synthetic fun associatedEventId (Lio/sentry/protocol/SentryId;)Lio/sentry/android/core/SentryUserFeedbackForm$Builder; public fun configurator (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)Lio/sentry/android/core/SentryUserFeedbackDialog$Builder; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java index 74f112b27fc..ddc757503ec 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java @@ -62,8 +62,7 @@ public Builder(Context context, int themeResId) { */ @Deprecated public Builder( - final @NotNull Context context, - final @Nullable SentryUserFeedbackForm.OptionsConfiguration configuration) { + final @NotNull Context context, final @Nullable OptionsConfiguration configuration) { super(context, configuration); } @@ -80,7 +79,7 @@ public Builder( public Builder( final @NotNull Context context, final int themeResId, - final @Nullable SentryUserFeedbackForm.OptionsConfiguration configuration) { + final @Nullable OptionsConfiguration configuration) { super(context, themeResId, configuration); } From 2631fbad311fce7fd1853298cd9f2d3b23cdb30c Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 29 Apr 2026 17:13:02 +0200 Subject: [PATCH 03/18] Make internal ctor package-private --- sentry/src/main/java/io/sentry/SentryFeedbackOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java b/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java index fcaa919d725..a72b352317e 100644 --- a/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java +++ b/sentry/src/main/java/io/sentry/SentryFeedbackOptions.java @@ -93,7 +93,7 @@ public final class SentryFeedbackOptions { private @NotNull IFormHandler iFormHandler; - public SentryFeedbackOptions(@NotNull IFormHandler iFormHandler) { + SentryFeedbackOptions(@NotNull IFormHandler iFormHandler) { this.iFormHandler = iFormHandler; } From 725827b4c4fb201ffad20f1b96bfb37401dc7abb Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 29 Apr 2026 17:19:51 +0200 Subject: [PATCH 04/18] Add missing deprecated annotaiton --- .../main/java/io/sentry/android/core/SentryAndroidOptions.java | 2 +- .../java/io/sentry/android/core/SentryUserFeedbackDialog.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index 8fe702aad50..4a9ede53eae 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -758,7 +758,7 @@ public void showForm( return; } - new SentryUserFeedbackForm.Builder(activity) + new SentryUserFeedbackDialog.Builder(activity) .associatedEventId(associatedEventId) .configurator(configurator) .create() diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java index ddc757503ec..155464b7b73 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java @@ -98,6 +98,7 @@ public Builder associatedEventId(final @Nullable SentryId associatedEventId) { return this; } + @Deprecated @Override public SentryUserFeedbackDialog create() { return new SentryUserFeedbackDialog( From 3c9dfc27a7a095a54d0ee704065fb3d23002b649 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 29 Apr 2026 17:26:49 +0200 Subject: [PATCH 05/18] Fix api --- .../main/java/io/sentry/android/core/SentryAndroidOptions.java | 2 +- sentry/api/sentry.api | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index 4a9ede53eae..8fe702aad50 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -758,7 +758,7 @@ public void showForm( return; } - new SentryUserFeedbackDialog.Builder(activity) + new SentryUserFeedbackForm.Builder(activity) .associatedEventId(associatedEventId) .configurator(configurator) .create() diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 967d19d7882..6d614626d6d 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3154,7 +3154,6 @@ public final class io/sentry/SentryExecutorService : io/sentry/ISentryExecutorSe } public final class io/sentry/SentryFeedbackOptions { - public fun (Lio/sentry/SentryFeedbackOptions$IFormHandler;)V public fun (Lio/sentry/SentryFeedbackOptions;)V public fun getCancelButtonLabel ()Ljava/lang/CharSequence; public fun getEmailLabel ()Ljava/lang/CharSequence; From 84fdc825b677a5a1aa42ed1f98b57d885b3fd772 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 29 Apr 2026 17:46:05 +0200 Subject: [PATCH 06/18] docs(changelog): Add deprecation entry for feedback Dialog to Form rename Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b5b7d1686..f40c7d174c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Deprecations + +- Rename `SentryUserFeedbackDialog` to `SentryUserFeedbackForm` and `Sentry.showUserFeedbackDialog()` to `Sentry.showUserFeedbackForm()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) + - The old `SentryUserFeedbackDialog` class and `Sentry.showUserFeedbackDialog()` methods are deprecated but still work + ### Dependencies - Bump Native SDK from v0.13.7 to v0.13.8 ([#5334](https://github.com/getsentry/sentry-java/pull/5334)) From 3eeb97806a14974cc7a791294106572808d8714f Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 29 Apr 2026 17:46:32 +0200 Subject: [PATCH 07/18] docs(changelog): Note removal in next major version Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f40c7d174c3..36d16643018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Deprecations - Rename `SentryUserFeedbackDialog` to `SentryUserFeedbackForm` and `Sentry.showUserFeedbackDialog()` to `Sentry.showUserFeedbackForm()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) - - The old `SentryUserFeedbackDialog` class and `Sentry.showUserFeedbackDialog()` methods are deprecated but still work + - The old `SentryUserFeedbackDialog` class and `Sentry.showUserFeedbackDialog()` methods are deprecated but still work and will be removed in the next major version ### Dependencies From 630238a0beebc37f533db7251d675b027f1840f7 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:07:45 +0200 Subject: [PATCH 08/18] feat(feedback): Add Sentry.feedback() API Introduce IFeedbackApi with showForm() and capture() methods, accessible via Sentry.feedback(). This consolidates all feedback operations under a single API entry point. Deprecate Sentry.showUserFeedbackForm(), Sentry.showUserFeedbackDialog(), Sentry.captureFeedback(), and Sentry.captureUserFeedback() in favor of the new Sentry.feedback() API. All deprecated methods will be removed in the next major version. Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 8 +- .../android/core/SentryUserFeedbackForm.java | 2 +- .../uitest/android/UserFeedbackUiTest.kt | 4 +- .../compose/SentryUserFeedbackButton.kt | 2 +- sentry/api/sentry.api | 20 +++++ .../src/main/java/io/sentry/IFeedbackApi.java | 29 +++++++ .../main/java/io/sentry/NoOpFeedbackApi.java | 46 ++++++++++ .../main/java/io/sentry/ScopesAdapter.java | 7 +- sentry/src/main/java/io/sentry/Sentry.java | 86 ++++++++++++------- .../java/io/sentry/SentryFeedbackApi.java | 45 ++++++++++ sentry/src/test/java/io/sentry/SentryTest.kt | 12 +-- 11 files changed, 217 insertions(+), 44 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/IFeedbackApi.java create mode 100644 sentry/src/main/java/io/sentry/NoOpFeedbackApi.java create mode 100644 sentry/src/main/java/io/sentry/SentryFeedbackApi.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d16643018..33961b83d65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,12 @@ ### Deprecations -- Rename `SentryUserFeedbackDialog` to `SentryUserFeedbackForm` and `Sentry.showUserFeedbackDialog()` to `Sentry.showUserFeedbackForm()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) - - The old `SentryUserFeedbackDialog` class and `Sentry.showUserFeedbackDialog()` methods are deprecated but still work and will be removed in the next major version +- Add `Sentry.feedback()` API for `showForm()` and `capture()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) + - `Sentry.showUserFeedbackDialog()` / `Sentry.showUserFeedbackForm()` are deprecated in favor of `Sentry.feedback().showForm()` + - `Sentry.captureFeedback()` is deprecated in favor of `Sentry.feedback().capture()` + - `Sentry.captureUserFeedback()` is deprecated in favor of `Sentry.feedback().capture()` with the new `Feedback` type + - `SentryUserFeedbackDialog` is deprecated in favor of `SentryUserFeedbackForm` + - All deprecated APIs will be removed in the next major version ### Dependencies diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java index 2ad1511175f..0babe475491 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackForm.java @@ -162,7 +162,7 @@ protected void onCreate(Bundle savedInstanceState) { } // Capture the feedback. If the ID is empty, it means that the feedback was not sent - final @NotNull SentryId id = Sentry.captureFeedback(feedback); + final @NotNull SentryId id = Sentry.feedback().capture(feedback); if (!id.equals(SentryId.EMPTY_ID)) { Toast.makeText( getContext(), feedbackOptions.getSuccessMessageText(), Toast.LENGTH_SHORT) diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt index 4942abb778d..93d6a8423d7 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt @@ -56,14 +56,14 @@ class UserFeedbackUiTest : BaseUiTest() { @Test fun userFeedbackNotShownWhenSdkDisabledViaApi() { - launchActivity().onActivity { Sentry.showUserFeedbackForm() } + launchActivity().onActivity { Sentry.feedback().showForm() } onView(withId(R.id.sentry_dialog_user_feedback_layout)).check(doesNotExist()) } @Test fun userFeedbackShownViaApi() { initSentry() - launchActivity().onActivity { Sentry.showUserFeedbackForm() } + launchActivity().onActivity { Sentry.feedback().showForm() } onView(withId(R.id.sentry_dialog_user_feedback_layout)) .inRoot(isDialog()) diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt index 8ded14f0765..9b2907cbec6 100644 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt @@ -21,7 +21,7 @@ public fun SentryUserFeedbackButton( text: String = "Report a Bug", configurator: SentryFeedbackOptions.OptionsConfigurator? = null, ) { - Button(modifier = modifier, onClick = { Sentry.showUserFeedbackForm(configurator) }) { + Button(modifier = modifier, onClick = { Sentry.feedback().showForm(configurator) }) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 6d614626d6d..0e6662dc297 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -834,6 +834,15 @@ public abstract interface class io/sentry/IEnvelopeSender { public abstract fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V } +public abstract interface class io/sentry/IFeedbackApi { + public abstract fun capture (Lio/sentry/protocol/Feedback;)Lio/sentry/protocol/SentryId; + public abstract fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public abstract fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public abstract fun showForm ()V + public abstract fun showForm (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public abstract fun showForm (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V +} + public abstract interface class io/sentry/IHub : io/sentry/IScopes { } @@ -1569,6 +1578,16 @@ public final class io/sentry/NoOpEnvelopeReader : io/sentry/IEnvelopeReader { public fun read (Ljava/io/InputStream;)Lio/sentry/SentryEnvelope; } +public final class io/sentry/NoOpFeedbackApi : io/sentry/IFeedbackApi { + public fun capture (Lio/sentry/protocol/Feedback;)Lio/sentry/protocol/SentryId; + public fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public static fun getInstance ()Lio/sentry/NoOpFeedbackApi; + public fun showForm ()V + public fun showForm (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public fun showForm (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V +} + public final class io/sentry/NoOpHub : io/sentry/IHub { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V @@ -2720,6 +2739,7 @@ public final class io/sentry/Sentry { public static fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public static fun distribution ()Lio/sentry/IDistributionApi; public static fun endSession ()V + public static fun feedback ()Lio/sentry/IFeedbackApi; public static fun flush (J)V public static fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public static fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; diff --git a/sentry/src/main/java/io/sentry/IFeedbackApi.java b/sentry/src/main/java/io/sentry/IFeedbackApi.java new file mode 100644 index 00000000000..5c78880b1a4 --- /dev/null +++ b/sentry/src/main/java/io/sentry/IFeedbackApi.java @@ -0,0 +1,29 @@ +package io.sentry; + +import io.sentry.protocol.Feedback; +import io.sentry.protocol.SentryId; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface IFeedbackApi { + + void showForm(); + + void showForm(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator); + + void showForm( + final @Nullable SentryId associatedEventId, + final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator); + + @NotNull + SentryId capture(final @NotNull Feedback feedback); + + @NotNull + SentryId capture(final @NotNull Feedback feedback, final @Nullable Hint hint); + + @NotNull + SentryId capture( + final @NotNull Feedback feedback, + final @Nullable Hint hint, + final @Nullable ScopeCallback callback); +} diff --git a/sentry/src/main/java/io/sentry/NoOpFeedbackApi.java b/sentry/src/main/java/io/sentry/NoOpFeedbackApi.java new file mode 100644 index 00000000000..518947658a5 --- /dev/null +++ b/sentry/src/main/java/io/sentry/NoOpFeedbackApi.java @@ -0,0 +1,46 @@ +package io.sentry; + +import io.sentry.protocol.Feedback; +import io.sentry.protocol.SentryId; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class NoOpFeedbackApi implements IFeedbackApi { + + private static final NoOpFeedbackApi instance = new NoOpFeedbackApi(); + + private NoOpFeedbackApi() {} + + public static NoOpFeedbackApi getInstance() { + return instance; + } + + @Override + public void showForm() {} + + @Override + public void showForm(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) {} + + @Override + public void showForm( + final @Nullable SentryId associatedEventId, + final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) {} + + @Override + public @NotNull SentryId capture(final @NotNull Feedback feedback) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId capture(final @NotNull Feedback feedback, final @Nullable Hint hint) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId capture( + final @NotNull Feedback feedback, + final @Nullable Hint hint, + final @Nullable ScopeCallback callback) { + return SentryId.EMPTY_ID; + } +} diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index b66b681a332..b7a73b13d8c 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -51,18 +51,18 @@ public boolean isEnabled() { @Override public @NotNull SentryId captureFeedback(@NotNull Feedback feedback) { - return Sentry.captureFeedback(feedback); + return Sentry.feedback().capture(feedback); } @Override public @NotNull SentryId captureFeedback(@NotNull Feedback feedback, @Nullable Hint hint) { - return Sentry.captureFeedback(feedback, hint); + return Sentry.feedback().capture(feedback, hint); } @Override public @NotNull SentryId captureFeedback( @NotNull Feedback feedback, @Nullable Hint hint, @Nullable ScopeCallback callback) { - return Sentry.captureFeedback(feedback, hint, callback); + return Sentry.feedback().capture(feedback, hint, callback); } @ApiStatus.Internal @@ -82,6 +82,7 @@ public boolean isEnabled() { return Sentry.captureException(throwable, hint, callback); } + @SuppressWarnings("deprecation") @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { Sentry.captureUserFeedback(userFeedback); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 5d4b2adba56..fd29742d2b3 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -827,40 +827,37 @@ public static void close() { } /** - * Captures the feedback. - * - * @param feedback The feedback to send. - * @return The Id (SentryId object) of the event + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#capture(Feedback) capture(feedback)} + * instead. */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static @NotNull SentryId captureFeedback(final @NotNull Feedback feedback) { - return getCurrentScopes().captureFeedback(feedback); + return feedback().capture(feedback); } /** - * Captures the feedback. - * - * @param feedback The feedback to send. - * @param hint An optional hint to be applied to the event. - * @return The Id (SentryId object) of the event + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#capture(Feedback, Hint) + * capture(feedback, hint)} instead. */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static @NotNull SentryId captureFeedback( final @NotNull Feedback feedback, final @Nullable Hint hint) { - return getCurrentScopes().captureFeedback(feedback, hint); + return feedback().capture(feedback, hint); } /** - * Captures the feedback. - * - * @param feedback The feedback to send. - * @param hint An optional hint to be applied to the event. - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#capture(Feedback, Hint, ScopeCallback) + * capture(feedback, hint, callback)} instead. */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static @NotNull SentryId captureFeedback( final @NotNull Feedback feedback, final @Nullable Hint hint, final @Nullable ScopeCallback callback) { - return getCurrentScopes().captureFeedback(feedback, hint, callback); + return feedback().capture(feedback, hint, callback); } /** @@ -916,7 +913,11 @@ public static void close() { * Captures a manually created user feedback and sends it to Sentry. * * @param userFeedback The user feedback to send to Sentry. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#capture(Feedback) capture(feedback)} + * with the new {@link Feedback} type instead. */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static void captureUserFeedback(final @NotNull UserFeedback userFeedback) { getCurrentScopes().captureUserFeedback(userFeedback); } @@ -1355,52 +1356,79 @@ public static IMetricsApi metrics() { return getCurrentScopes().metrics(); } + private static final @NotNull IFeedbackApi feedbackApi = new SentryFeedbackApi(); + + @NotNull + public static IFeedbackApi feedback() { + return feedbackApi; + } + + /** + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm() showForm()} instead. + */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackForm() { - showUserFeedbackForm(null); + feedback().showForm(); } + /** + * @deprecated Use {@link #feedback()}.{@link + * IFeedbackApi#showForm(SentryFeedbackOptions.OptionsConfigurator) showForm(configurator)} + * instead. + */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackForm( final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - showUserFeedbackForm(null, configurator); + feedback().showForm(configurator); } + /** + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm(SentryId, + * SentryFeedbackOptions.OptionsConfigurator) showForm(associatedEventId, configurator)} + * instead. + */ + @Deprecated + @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackForm( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - final @NotNull SentryOptions options = getCurrentScopes().getOptions(); - options.getFeedbackOptions().getFormHandler().showForm(associatedEventId, configurator); + feedback().showForm(associatedEventId, configurator); } /** - * @deprecated Use {@link #showUserFeedbackForm()} instead. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm() showForm()} instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog() { - showUserFeedbackForm(); + feedback().showForm(); } /** - * @deprecated Use {@link #showUserFeedbackForm(SentryFeedbackOptions.OptionsConfigurator)} + * @deprecated Use {@link #feedback()}.{@link + * IFeedbackApi#showForm(SentryFeedbackOptions.OptionsConfigurator) showForm(configurator)} * instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog( final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - showUserFeedbackForm(configurator); + feedback().showForm(configurator); } /** - * @deprecated Use {@link #showUserFeedbackForm(SentryId, - * SentryFeedbackOptions.OptionsConfigurator)} instead. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm(SentryId, + * SentryFeedbackOptions.OptionsConfigurator) showForm(associatedEventId, configurator)} + * instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - showUserFeedbackForm(associatedEventId, configurator); + feedback().showForm(associatedEventId, configurator); } /** diff --git a/sentry/src/main/java/io/sentry/SentryFeedbackApi.java b/sentry/src/main/java/io/sentry/SentryFeedbackApi.java new file mode 100644 index 00000000000..eceb8caa5de --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentryFeedbackApi.java @@ -0,0 +1,45 @@ +package io.sentry; + +import io.sentry.protocol.Feedback; +import io.sentry.protocol.SentryId; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class SentryFeedbackApi implements IFeedbackApi { + + @Override + public void showForm() { + showForm(null, null); + } + + @Override + public void showForm(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { + showForm(null, configurator); + } + + @Override + public void showForm( + final @Nullable SentryId associatedEventId, + final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { + final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); + options.getFeedbackOptions().getFormHandler().showForm(associatedEventId, configurator); + } + + @Override + public @NotNull SentryId capture(final @NotNull Feedback feedback) { + return Sentry.getCurrentScopes().captureFeedback(feedback); + } + + @Override + public @NotNull SentryId capture(final @NotNull Feedback feedback, final @Nullable Hint hint) { + return Sentry.getCurrentScopes().captureFeedback(feedback, hint); + } + + @Override + public @NotNull SentryId capture( + final @NotNull Feedback feedback, + final @Nullable Hint hint, + final @Nullable ScopeCallback callback) { + return Sentry.getCurrentScopes().captureFeedback(feedback, hint, callback); + } +} diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 2503b8c755c..ad887d3ffbd 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -1504,30 +1504,30 @@ class SentryTest { } @Test - fun `showUserFeedbackForm forwards to feedbackOptions_formHandler`() { + fun `feedback showForm forwards to feedbackOptions_formHandler`() { val mockFormHandler = mock() initForTest { it.dsn = dsn it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.showUserFeedbackForm() + Sentry.feedback().showForm() verify(mockFormHandler).showForm(eq(null), eq(null)) } @Test - fun `showUserFeedbackForm forwards to feedbackOptions_formHandler with configurator`() { + fun `feedback showForm forwards to feedbackOptions_formHandler with configurator`() { val mockFormHandler = mock() val configurator = mock() initForTest { it.dsn = dsn it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.showUserFeedbackForm(configurator) + Sentry.feedback().showForm(configurator) verify(mockFormHandler).showForm(eq(null), eq(configurator)) } @Test - fun `showUserFeedbackForm forwards to feedbackOptions_formHandler with associatedEventId and configurator`() { + fun `feedback showForm forwards to feedbackOptions_formHandler with associatedEventId and configurator`() { val mockFormHandler = mock() val configurator = mock() val associatedEventId = SentryId() @@ -1535,7 +1535,7 @@ class SentryTest { it.dsn = dsn it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.showUserFeedbackForm(associatedEventId, configurator) + Sentry.feedback().showForm(associatedEventId, configurator) verify(mockFormHandler).showForm(eq(associatedEventId), eq(configurator)) } From 174d7464678f98e24d25f9763ca09904d37238d8 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:08:36 +0200 Subject: [PATCH 09/18] docs(changelog): Update section to Features and remove unpublished API Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33961b83d65..3391da99415 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,10 @@ ## Unreleased -### Deprecations +### Features - Add `Sentry.feedback()` API for `showForm()` and `capture()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) - - `Sentry.showUserFeedbackDialog()` / `Sentry.showUserFeedbackForm()` are deprecated in favor of `Sentry.feedback().showForm()` + - `Sentry.showUserFeedbackDialog()` is deprecated in favor of `Sentry.feedback().showForm()` - `Sentry.captureFeedback()` is deprecated in favor of `Sentry.feedback().capture()` - `Sentry.captureUserFeedback()` is deprecated in favor of `Sentry.feedback().capture()` with the new `Feedback` type - `SentryUserFeedbackDialog` is deprecated in favor of `SentryUserFeedbackForm` From 28d552171b1636d87456b44a3299b375ddfc7ce4 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:13:00 +0200 Subject: [PATCH 10/18] ref(feedback): Move FeedbackApi to IScopes Add feedback() method to IScopes, matching the pattern used by logger() and metrics(). FeedbackApi takes an IScopes reference instead of using Sentry.getCurrentScopes() statically. Implemented in Scopes, NoOpScopes, NoOpHub, HubAdapter, HubScopesWrapper, and ScopesAdapter. Sentry.feedback() now delegates to getCurrentScopes().feedback(). Co-Authored-By: Claude Opus 4.6 --- sentry/api/sentry.api | 7 +++++++ .../{SentryFeedbackApi.java => FeedbackApi.java} | 16 +++++++++++----- sentry/src/main/java/io/sentry/HubAdapter.java | 5 +++++ .../main/java/io/sentry/HubScopesWrapper.java | 5 +++++ sentry/src/main/java/io/sentry/IScopes.java | 3 +++ sentry/src/main/java/io/sentry/NoOpHub.java | 5 +++++ sentry/src/main/java/io/sentry/NoOpScopes.java | 5 +++++ sentry/src/main/java/io/sentry/Scopes.java | 7 +++++++ .../src/main/java/io/sentry/ScopesAdapter.java | 5 +++++ sentry/src/main/java/io/sentry/Sentry.java | 4 +--- 10 files changed, 54 insertions(+), 8 deletions(-) rename sentry/src/main/java/io/sentry/{SentryFeedbackApi.java => FeedbackApi.java} (72%) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 0e6662dc297..8a819246090 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -664,6 +664,7 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V + public fun feedback ()Lio/sentry/IFeedbackApi; public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; @@ -740,6 +741,7 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V + public fun feedback ()Lio/sentry/IFeedbackApi; public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; @@ -1021,6 +1023,7 @@ public abstract interface class io/sentry/IScopes { public abstract fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public abstract fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public abstract fun endSession ()V + public abstract fun feedback ()Lio/sentry/IFeedbackApi; public abstract fun flush (J)V public abstract fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public abstract fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; @@ -1614,6 +1617,7 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V + public fun feedback ()Lio/sentry/IFeedbackApi; public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; @@ -1800,6 +1804,7 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V + public fun feedback ()Lio/sentry/IFeedbackApi; public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; @@ -2536,6 +2541,7 @@ public final class io/sentry/Scopes : io/sentry/IScopes { public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V + public fun feedback ()Lio/sentry/IFeedbackApi; public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; @@ -2615,6 +2621,7 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V + public fun feedback ()Lio/sentry/IFeedbackApi; public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; diff --git a/sentry/src/main/java/io/sentry/SentryFeedbackApi.java b/sentry/src/main/java/io/sentry/FeedbackApi.java similarity index 72% rename from sentry/src/main/java/io/sentry/SentryFeedbackApi.java rename to sentry/src/main/java/io/sentry/FeedbackApi.java index eceb8caa5de..e39e32e0431 100644 --- a/sentry/src/main/java/io/sentry/SentryFeedbackApi.java +++ b/sentry/src/main/java/io/sentry/FeedbackApi.java @@ -5,7 +5,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -final class SentryFeedbackApi implements IFeedbackApi { +final class FeedbackApi implements IFeedbackApi { + + private final @NotNull IScopes scopes; + + FeedbackApi(final @NotNull IScopes scopes) { + this.scopes = scopes; + } @Override public void showForm() { @@ -21,18 +27,18 @@ public void showForm(final @Nullable SentryFeedbackOptions.OptionsConfigurator c public void showForm( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); + final @NotNull SentryOptions options = scopes.getOptions(); options.getFeedbackOptions().getFormHandler().showForm(associatedEventId, configurator); } @Override public @NotNull SentryId capture(final @NotNull Feedback feedback) { - return Sentry.getCurrentScopes().captureFeedback(feedback); + return scopes.captureFeedback(feedback); } @Override public @NotNull SentryId capture(final @NotNull Feedback feedback, final @Nullable Hint hint) { - return Sentry.getCurrentScopes().captureFeedback(feedback, hint); + return scopes.captureFeedback(feedback, hint); } @Override @@ -40,6 +46,6 @@ public void showForm( final @NotNull Feedback feedback, final @Nullable Hint hint, final @Nullable ScopeCallback callback) { - return Sentry.getCurrentScopes().captureFeedback(feedback, hint, callback); + return scopes.captureFeedback(feedback, hint, callback); } } diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index cf90eb1fe65..5e2d91a9ae8 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -395,6 +395,11 @@ public void reportFullyDisplayed() { return Sentry.getCurrentScopes().metrics(); } + @Override + public @NotNull IFeedbackApi feedback() { + return Sentry.getCurrentScopes().feedback(); + } + @Override public void setAttribute(final @Nullable String key, final @Nullable Object value) { Sentry.setAttribute(key, value); diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 66a34b4dc36..00395292fd5 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -380,6 +380,11 @@ public void reportFullyDisplayed() { return scopes.metrics(); } + @Override + public @NotNull IFeedbackApi feedback() { + return scopes.feedback(); + } + @Override public void setAttribute(final @Nullable String key, final @Nullable Object value) { scopes.setAttribute(key, value); diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index b1b437f72e5..c4ecd7a64e6 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -748,6 +748,9 @@ default boolean isNoOp() { @NotNull IMetricsApi metrics(); + @NotNull + IFeedbackApi feedback(); + /** * Sets an attribute. * diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 4a02be1bd40..235729f65e6 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -338,6 +338,11 @@ public boolean isNoOp() { return NoOpMetricsApi.getInstance(); } + @Override + public @NotNull IFeedbackApi feedback() { + return NoOpFeedbackApi.getInstance(); + } + @Override public void setAttribute(final @Nullable String key, final @Nullable Object value) {} diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index 1ae357d502e..a443e05c9e7 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -336,6 +336,11 @@ public boolean isNoOp() { return NoOpMetricsApi.getInstance(); } + @Override + public @NotNull IFeedbackApi feedback() { + return NoOpFeedbackApi.getInstance(); + } + @Override public void setAttribute(final @Nullable String key, final @Nullable Object value) {} diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 82c03feac4b..37840c7920a 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -34,6 +34,7 @@ public final class Scopes implements IScopes { private final @NotNull CombinedScopeView combinedScope; private final @NotNull ILoggerApi logger; private final @NotNull IMetricsApi metrics; + private final @NotNull IFeedbackApi feedbackApi; public Scopes( final @NotNull IScope scope, @@ -61,6 +62,7 @@ private Scopes( this.compositePerformanceCollector = options.getCompositePerformanceCollector(); this.logger = new LoggerApi(this); this.metrics = new MetricsApi(this); + this.feedbackApi = new FeedbackApi(this); } public @NotNull String getCreator() { @@ -1245,6 +1247,11 @@ public void reportFullyDisplayed() { return metrics; } + @Override + public @NotNull IFeedbackApi feedback() { + return feedbackApi; + } + @Override public void setAttribute(final @Nullable String key, final @Nullable Object value) { if (!isEnabled()) { diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index b7a73b13d8c..694ff6e494f 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -393,6 +393,11 @@ public void reportFullyDisplayed() { return Sentry.getCurrentScopes().metrics(); } + @Override + public @NotNull IFeedbackApi feedback() { + return Sentry.getCurrentScopes().feedback(); + } + @Override public void setAttribute(final @Nullable String key, final @Nullable Object value) { Sentry.setAttribute(key, value); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index fd29742d2b3..eb44a3c32d0 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -1356,11 +1356,9 @@ public static IMetricsApi metrics() { return getCurrentScopes().metrics(); } - private static final @NotNull IFeedbackApi feedbackApi = new SentryFeedbackApi(); - @NotNull public static IFeedbackApi feedback() { - return feedbackApi; + return getCurrentScopes().feedback(); } /** From 29febd780a92a055a85a7ae265dbc5930b58faf4 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:18:32 +0200 Subject: [PATCH 11/18] ref: Rename showForm() to show() on IFeedbackApi Since the method is already namespaced under feedback(), the extra "Form" suffix is redundant. This aligns with the convention used by logger() and metrics(). Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 4 +-- .../uitest/android/UserFeedbackUiTest.kt | 4 +-- .../compose/SentryUserFeedbackButton.kt | 2 +- sentry/api/sentry.api | 12 +++---- .../src/main/java/io/sentry/FeedbackApi.java | 10 +++--- .../src/main/java/io/sentry/IFeedbackApi.java | 6 ++-- .../main/java/io/sentry/NoOpFeedbackApi.java | 6 ++-- sentry/src/main/java/io/sentry/Sentry.java | 32 ++++++++----------- sentry/src/test/java/io/sentry/SentryTest.kt | 12 +++---- 9 files changed, 42 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3391da99415..84bb4b3e0f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ ### Features -- Add `Sentry.feedback()` API for `showForm()` and `capture()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) - - `Sentry.showUserFeedbackDialog()` is deprecated in favor of `Sentry.feedback().showForm()` +- Add `Sentry.feedback()` API for `show()` and `capture()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) + - `Sentry.showUserFeedbackDialog()` is deprecated in favor of `Sentry.feedback().show()` - `Sentry.captureFeedback()` is deprecated in favor of `Sentry.feedback().capture()` - `Sentry.captureUserFeedback()` is deprecated in favor of `Sentry.feedback().capture()` with the new `Feedback` type - `SentryUserFeedbackDialog` is deprecated in favor of `SentryUserFeedbackForm` diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt index 93d6a8423d7..bfcaf2845b3 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt @@ -56,14 +56,14 @@ class UserFeedbackUiTest : BaseUiTest() { @Test fun userFeedbackNotShownWhenSdkDisabledViaApi() { - launchActivity().onActivity { Sentry.feedback().showForm() } + launchActivity().onActivity { Sentry.feedback().show() } onView(withId(R.id.sentry_dialog_user_feedback_layout)).check(doesNotExist()) } @Test fun userFeedbackShownViaApi() { initSentry() - launchActivity().onActivity { Sentry.feedback().showForm() } + launchActivity().onActivity { Sentry.feedback().show() } onView(withId(R.id.sentry_dialog_user_feedback_layout)) .inRoot(isDialog()) diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt index 9b2907cbec6..0836c826d32 100644 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt @@ -21,7 +21,7 @@ public fun SentryUserFeedbackButton( text: String = "Report a Bug", configurator: SentryFeedbackOptions.OptionsConfigurator? = null, ) { - Button(modifier = modifier, onClick = { Sentry.feedback().showForm(configurator) }) { + Button(modifier = modifier, onClick = { Sentry.feedback().show(configurator) }) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 8a819246090..b4cab4edf25 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -840,9 +840,9 @@ public abstract interface class io/sentry/IFeedbackApi { public abstract fun capture (Lio/sentry/protocol/Feedback;)Lio/sentry/protocol/SentryId; public abstract fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public abstract fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public abstract fun showForm ()V - public abstract fun showForm (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V - public abstract fun showForm (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public abstract fun show ()V + public abstract fun show (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public abstract fun show (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V } public abstract interface class io/sentry/IHub : io/sentry/IScopes { @@ -1586,9 +1586,9 @@ public final class io/sentry/NoOpFeedbackApi : io/sentry/IFeedbackApi { public fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun capture (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun getInstance ()Lio/sentry/NoOpFeedbackApi; - public fun showForm ()V - public fun showForm (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V - public fun showForm (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public fun show ()V + public fun show (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V + public fun show (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V } public final class io/sentry/NoOpHub : io/sentry/IHub { diff --git a/sentry/src/main/java/io/sentry/FeedbackApi.java b/sentry/src/main/java/io/sentry/FeedbackApi.java index e39e32e0431..b8b8a3c9b9a 100644 --- a/sentry/src/main/java/io/sentry/FeedbackApi.java +++ b/sentry/src/main/java/io/sentry/FeedbackApi.java @@ -14,17 +14,17 @@ final class FeedbackApi implements IFeedbackApi { } @Override - public void showForm() { - showForm(null, null); + public void show() { + show(null, null); } @Override - public void showForm(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - showForm(null, configurator); + public void show(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { + show(null, configurator); } @Override - public void showForm( + public void show( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { final @NotNull SentryOptions options = scopes.getOptions(); diff --git a/sentry/src/main/java/io/sentry/IFeedbackApi.java b/sentry/src/main/java/io/sentry/IFeedbackApi.java index 5c78880b1a4..5bab630fa8b 100644 --- a/sentry/src/main/java/io/sentry/IFeedbackApi.java +++ b/sentry/src/main/java/io/sentry/IFeedbackApi.java @@ -7,11 +7,11 @@ public interface IFeedbackApi { - void showForm(); + void show(); - void showForm(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator); + void show(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator); - void showForm( + void show( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator); diff --git a/sentry/src/main/java/io/sentry/NoOpFeedbackApi.java b/sentry/src/main/java/io/sentry/NoOpFeedbackApi.java index 518947658a5..bdef5d37590 100644 --- a/sentry/src/main/java/io/sentry/NoOpFeedbackApi.java +++ b/sentry/src/main/java/io/sentry/NoOpFeedbackApi.java @@ -16,13 +16,13 @@ public static NoOpFeedbackApi getInstance() { } @Override - public void showForm() {} + public void show() {} @Override - public void showForm(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) {} + public void show(final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) {} @Override - public void showForm( + public void show( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) {} diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index eb44a3c32d0..dd1812300ff 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -1362,71 +1362,67 @@ public static IFeedbackApi feedback() { } /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm() showForm()} instead. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show() show()} instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackForm() { - feedback().showForm(); + feedback().show(); } /** * @deprecated Use {@link #feedback()}.{@link - * IFeedbackApi#showForm(SentryFeedbackOptions.OptionsConfigurator) showForm(configurator)} - * instead. + * IFeedbackApi#show(SentryFeedbackOptions.OptionsConfigurator) show(configurator)} instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackForm( final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - feedback().showForm(configurator); + feedback().show(configurator); } /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm(SentryId, - * SentryFeedbackOptions.OptionsConfigurator) showForm(associatedEventId, configurator)} - * instead. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show(SentryId, + * SentryFeedbackOptions.OptionsConfigurator) show(associatedEventId, configurator)} instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackForm( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - feedback().showForm(associatedEventId, configurator); + feedback().show(associatedEventId, configurator); } /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm() showForm()} instead. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show() show()} instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog() { - feedback().showForm(); + feedback().show(); } /** * @deprecated Use {@link #feedback()}.{@link - * IFeedbackApi#showForm(SentryFeedbackOptions.OptionsConfigurator) showForm(configurator)} - * instead. + * IFeedbackApi#show(SentryFeedbackOptions.OptionsConfigurator) show(configurator)} instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog( final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - feedback().showForm(configurator); + feedback().show(configurator); } /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#showForm(SentryId, - * SentryFeedbackOptions.OptionsConfigurator) showForm(associatedEventId, configurator)} - * instead. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show(SentryId, + * SentryFeedbackOptions.OptionsConfigurator) show(associatedEventId, configurator)} instead. */ @Deprecated @SuppressWarnings("InlineMeSuggester") public static void showUserFeedbackDialog( final @Nullable SentryId associatedEventId, final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - feedback().showForm(associatedEventId, configurator); + feedback().show(associatedEventId, configurator); } /** diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index ad887d3ffbd..3712b083de7 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -1504,30 +1504,30 @@ class SentryTest { } @Test - fun `feedback showForm forwards to feedbackOptions_formHandler`() { + fun `feedback show forwards to feedbackOptions_formHandler`() { val mockFormHandler = mock() initForTest { it.dsn = dsn it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.feedback().showForm() + Sentry.feedback().show() verify(mockFormHandler).showForm(eq(null), eq(null)) } @Test - fun `feedback showForm forwards to feedbackOptions_formHandler with configurator`() { + fun `feedback show forwards to feedbackOptions_formHandler with configurator`() { val mockFormHandler = mock() val configurator = mock() initForTest { it.dsn = dsn it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.feedback().showForm(configurator) + Sentry.feedback().show(configurator) verify(mockFormHandler).showForm(eq(null), eq(configurator)) } @Test - fun `feedback showForm forwards to feedbackOptions_formHandler with associatedEventId and configurator`() { + fun `feedback show forwards to feedbackOptions_formHandler with associatedEventId and configurator`() { val mockFormHandler = mock() val configurator = mock() val associatedEventId = SentryId() @@ -1535,7 +1535,7 @@ class SentryTest { it.dsn = dsn it.feedbackOptions.setFormHandler(mockFormHandler) } - Sentry.feedback().showForm(associatedEventId, configurator) + Sentry.feedback().show(associatedEventId, configurator) verify(mockFormHandler).showForm(eq(associatedEventId), eq(configurator)) } From 6a0b779a0636e0fc65d921c0411b696d4ad5e8a7 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:27:23 +0200 Subject: [PATCH 12/18] chore: Deprecate UserFeedback and captureUserFeedback, delete showUserFeedbackForm Deprecate the old `UserFeedback` class and `captureUserFeedback()` across IScopes, ISentryClient, and all implementations in favor of `Sentry.feedback().capture()` with the new `Feedback` type. Delete `Sentry.showUserFeedbackForm()` (3 overloads) as it was never published. Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 2 +- sentry/api/sentry.api | 3 -- .../src/main/java/io/sentry/HubAdapter.java | 2 ++ .../main/java/io/sentry/HubScopesWrapper.java | 2 ++ sentry/src/main/java/io/sentry/IScopes.java | 3 ++ .../main/java/io/sentry/ISentryClient.java | 3 ++ .../main/java/io/sentry/JsonSerializer.java | 1 + sentry/src/main/java/io/sentry/NoOpHub.java | 2 ++ .../src/main/java/io/sentry/NoOpScopes.java | 2 ++ .../main/java/io/sentry/NoOpSentryClient.java | 2 ++ sentry/src/main/java/io/sentry/Scopes.java | 2 ++ .../main/java/io/sentry/ScopesAdapter.java | 1 + sentry/src/main/java/io/sentry/Sentry.java | 32 ------------------- .../src/main/java/io/sentry/SentryClient.java | 3 ++ .../java/io/sentry/SentryEnvelopeItem.java | 1 + .../src/main/java/io/sentry/UserFeedback.java | 8 ++++- 16 files changed, 32 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84bb4b3e0f4..4d1a581769c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Add `Sentry.feedback()` API for `show()` and `capture()` ([#5349](https://github.com/getsentry/sentry-java/pull/5349)) - `Sentry.showUserFeedbackDialog()` is deprecated in favor of `Sentry.feedback().show()` - `Sentry.captureFeedback()` is deprecated in favor of `Sentry.feedback().capture()` - - `Sentry.captureUserFeedback()` is deprecated in favor of `Sentry.feedback().capture()` with the new `Feedback` type + - `Sentry.captureUserFeedback()` and `UserFeedback` are deprecated in favor of `Sentry.feedback().capture()` with the new `Feedback` type - `SentryUserFeedbackDialog` is deprecated in favor of `SentryUserFeedbackForm` - All deprecated APIs will be removed in the next major version diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index b4cab4edf25..8bd1e90e094 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2793,9 +2793,6 @@ public final class io/sentry/Sentry { public static fun showUserFeedbackDialog ()V public static fun showUserFeedbackDialog (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V public static fun showUserFeedbackDialog (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V - public static fun showUserFeedbackForm ()V - public static fun showUserFeedbackForm (Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V - public static fun showUserFeedbackForm (Lio/sentry/protocol/SentryId;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;)V public static fun startProfiler ()V public static fun startSession ()V public static fun startTransaction (Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 5e2d91a9ae8..c59cfca85e1 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -86,6 +86,8 @@ public boolean isEnabled() { return Sentry.captureException(throwable, hint, callback); } + @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { Sentry.captureUserFeedback(userFeedback); diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 00395292fd5..6e271d97e38 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -75,6 +75,8 @@ public boolean isEnabled() { return scopes.captureException(throwable, hint, callback); } + @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { scopes.captureUserFeedback(userFeedback); diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index c4ecd7a64e6..26ea0dcc3ea 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -217,7 +217,10 @@ SentryId captureException( * Captures a manually created user feedback and sends it to Sentry. * * @param userFeedback The user feedback to send to Sentry. + * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#capture(io.sentry.protocol.Feedback) + * capture(feedback)} with the new {@link io.sentry.protocol.Feedback} type instead. */ + @Deprecated void captureUserFeedback(@NotNull UserFeedback userFeedback); /** Starts a new session. If there's a running session, it ends it before starting the new one. */ diff --git a/sentry/src/main/java/io/sentry/ISentryClient.java b/sentry/src/main/java/io/sentry/ISentryClient.java index 98b6034bb78..2a1df15f812 100644 --- a/sentry/src/main/java/io/sentry/ISentryClient.java +++ b/sentry/src/main/java/io/sentry/ISentryClient.java @@ -174,7 +174,10 @@ SentryId captureReplayEvent( * Captures a manually created user feedback and sends it to Sentry. * * @param userFeedback The user feedback to send to Sentry. + * @deprecated Use {@link IFeedbackApi#capture(io.sentry.protocol.Feedback)} with the new {@link + * io.sentry.protocol.Feedback} type instead. */ + @Deprecated void captureUserFeedback(@NotNull UserFeedback userFeedback); /** diff --git a/sentry/src/main/java/io/sentry/JsonSerializer.java b/sentry/src/main/java/io/sentry/JsonSerializer.java index a0fa80879aa..2b24090d0cc 100644 --- a/sentry/src/main/java/io/sentry/JsonSerializer.java +++ b/sentry/src/main/java/io/sentry/JsonSerializer.java @@ -72,6 +72,7 @@ public final class JsonSerializer implements ISerializer { /** * All our custom deserializers need to be registered to be used with the deserializer instance. * */ + @SuppressWarnings("deprecation") public JsonSerializer(@NotNull SentryOptions options) { this.options = options; diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 235729f65e6..0cbe16c44ea 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -80,6 +80,8 @@ public boolean isEnabled() { return SentryId.EMPTY_ID; } + @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index a443e05c9e7..ee6ec5433e5 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -77,6 +77,8 @@ public boolean isEnabled() { return SentryId.EMPTY_ID; } + @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index 961ef9031be..3d9c689da8a 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -44,6 +44,8 @@ public void flush(long timeoutMillis) {} return SentryId.EMPTY_ID; } + @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 37840c7920a..374f5948856 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -346,6 +346,8 @@ private void assignTraceContext(final @NotNull SentryEvent event) { return sentryId; } + @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { if (!isEnabled()) { diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 694ff6e494f..30bca94e474 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -83,6 +83,7 @@ public boolean isEnabled() { } @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { Sentry.captureUserFeedback(userFeedback); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index dd1812300ff..919607e5879 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -1361,38 +1361,6 @@ public static IFeedbackApi feedback() { return getCurrentScopes().feedback(); } - /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show() show()} instead. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") - public static void showUserFeedbackForm() { - feedback().show(); - } - - /** - * @deprecated Use {@link #feedback()}.{@link - * IFeedbackApi#show(SentryFeedbackOptions.OptionsConfigurator) show(configurator)} instead. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") - public static void showUserFeedbackForm( - final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - feedback().show(configurator); - } - - /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show(SentryId, - * SentryFeedbackOptions.OptionsConfigurator) show(associatedEventId, configurator)} instead. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") - public static void showUserFeedbackForm( - final @Nullable SentryId associatedEventId, - final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - feedback().show(associatedEventId, configurator); - } - /** * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show() show()} instead. */ diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index b8178e35517..48f665b5c99 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -688,6 +688,8 @@ private SentryEvent processFeedbackEvent( return feedbackEvent; } + @SuppressWarnings("deprecation") + @Deprecated @Override public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { Objects.requireNonNull(userFeedback, "SentryEvent is required."); @@ -714,6 +716,7 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { } } + @SuppressWarnings("deprecation") private @NotNull SentryEnvelope buildEnvelope(final @NotNull UserFeedback userFeedback) { final List envelopeItems = new ArrayList<>(); diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index dd47d2b99d0..70933178a53 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -169,6 +169,7 @@ public final class SentryEnvelopeItem { } } + @SuppressWarnings("deprecation") public static SentryEnvelopeItem fromUserFeedback( final @NotNull ISerializer serializer, final @NotNull UserFeedback userFeedback) { Objects.requireNonNull(serializer, "ISerializer is required."); diff --git a/sentry/src/main/java/io/sentry/UserFeedback.java b/sentry/src/main/java/io/sentry/UserFeedback.java index b580744ee77..b9d0ade0f9d 100644 --- a/sentry/src/main/java/io/sentry/UserFeedback.java +++ b/sentry/src/main/java/io/sentry/UserFeedback.java @@ -8,7 +8,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** Adds additional information about what happened to an event. */ +/** + * Adds additional information about what happened to an event. + * + * @deprecated Use {@link io.sentry.protocol.Feedback} with {@link Sentry#feedback()}.{@link + * IFeedbackApi#capture(io.sentry.protocol.Feedback) capture(feedback)} instead. + */ +@Deprecated public final class UserFeedback implements JsonUnknown, JsonSerializable { private final SentryId eventId; From bce1a456b1d67c70ef984e716c202a8b681811d3 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:28:15 +0200 Subject: [PATCH 13/18] chore: Deprecate SentryEnvelopeItem.fromUserFeedback() Co-Authored-By: Claude Opus 4.6 --- sentry/src/main/java/io/sentry/SentryEnvelopeItem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index 70933178a53..7e7583daefc 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -170,6 +170,7 @@ public final class SentryEnvelopeItem { } @SuppressWarnings("deprecation") + @Deprecated public static SentryEnvelopeItem fromUserFeedback( final @NotNull ISerializer serializer, final @NotNull UserFeedback userFeedback) { Objects.requireNonNull(serializer, "ISerializer is required."); From 6f3a18f5168552a7f56b0eb3e0aaa5e1b0a484ff Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:30:33 +0200 Subject: [PATCH 14/18] chore: Deprecate SentryClient.buildEnvelope(UserFeedback) Co-Authored-By: Claude Opus 4.6 --- sentry/src/main/java/io/sentry/SentryClient.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 48f665b5c99..a80082f7da9 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -717,6 +717,7 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { } @SuppressWarnings("deprecation") + @Deprecated private @NotNull SentryEnvelope buildEnvelope(final @NotNull UserFeedback userFeedback) { final List envelopeItems = new ArrayList<>(); From 05ab6dbfc5575bb43f2da9e3640c912de6fd00e6 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 10:40:39 +0200 Subject: [PATCH 15/18] ref: Remove unnecessary SuppressWarnings("deprecation") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deprecated methods don't need to suppress deprecation warnings for referencing other deprecated types — the deprecation annotation itself is sufficient. Co-Authored-By: Claude Opus 4.6 --- sentry/src/main/java/io/sentry/HubAdapter.java | 1 - sentry/src/main/java/io/sentry/HubScopesWrapper.java | 1 - sentry/src/main/java/io/sentry/NoOpHub.java | 1 - sentry/src/main/java/io/sentry/NoOpScopes.java | 1 - sentry/src/main/java/io/sentry/NoOpSentryClient.java | 1 - sentry/src/main/java/io/sentry/Scopes.java | 1 - sentry/src/main/java/io/sentry/ScopesAdapter.java | 1 - sentry/src/main/java/io/sentry/SentryClient.java | 2 -- sentry/src/main/java/io/sentry/SentryEnvelopeItem.java | 1 - 9 files changed, 10 deletions(-) diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index c59cfca85e1..42ab6b317e3 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -86,7 +86,6 @@ public boolean isEnabled() { return Sentry.captureException(throwable, hint, callback); } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 6e271d97e38..271e1084bfb 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -75,7 +75,6 @@ public boolean isEnabled() { return scopes.captureException(throwable, hint, callback); } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 0cbe16c44ea..d5ef143d342 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -80,7 +80,6 @@ public boolean isEnabled() { return SentryId.EMPTY_ID; } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index ee6ec5433e5..345c74cc0ee 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -77,7 +77,6 @@ public boolean isEnabled() { return SentryId.EMPTY_ID; } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index 3d9c689da8a..ac9ff2344bf 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -44,7 +44,6 @@ public void flush(long timeoutMillis) {} return SentryId.EMPTY_ID; } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 374f5948856..3b67b94916e 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -346,7 +346,6 @@ private void assignTraceContext(final @NotNull SentryEvent event) { return sentryId; } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 30bca94e474..b697a950501 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -82,7 +82,6 @@ public boolean isEnabled() { return Sentry.captureException(throwable, hint, callback); } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index a80082f7da9..c99fcaeaa2f 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -688,7 +688,6 @@ private SentryEvent processFeedbackEvent( return feedbackEvent; } - @SuppressWarnings("deprecation") @Deprecated @Override public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { @@ -716,7 +715,6 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { } } - @SuppressWarnings("deprecation") @Deprecated private @NotNull SentryEnvelope buildEnvelope(final @NotNull UserFeedback userFeedback) { final List envelopeItems = new ArrayList<>(); diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index 7e7583daefc..dbbc36524db 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -169,7 +169,6 @@ public final class SentryEnvelopeItem { } } - @SuppressWarnings("deprecation") @Deprecated public static SentryEnvelopeItem fromUserFeedback( final @NotNull ISerializer serializer, final @NotNull UserFeedback userFeedback) { From f713580c6e160f3671ade19c956b6f1651e208c9 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 11:11:56 +0200 Subject: [PATCH 16/18] fix test --- sentry/src/test/java/io/sentry/HubAdapterTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sentry/src/test/java/io/sentry/HubAdapterTest.kt b/sentry/src/test/java/io/sentry/HubAdapterTest.kt index 9b97d8935f7..0dbb6a43c11 100644 --- a/sentry/src/test/java/io/sentry/HubAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/HubAdapterTest.kt @@ -14,6 +14,7 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.reset import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever class HubAdapterTest { val scopes: IScopes = mock() @@ -63,6 +64,7 @@ class HubAdapterTest { val hint = Hint() val scopeCallback = mock() val feedback = Feedback("message") + whenever(scopes.feedback()).thenReturn(FeedbackApi(scopes)) HubAdapter.getInstance().captureFeedback(feedback) verify(scopes).captureFeedback(eq(feedback)) From 1d28b4d0f1a26ee08f9815dcdc715a6f72ae3d8b Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 11:29:04 +0200 Subject: [PATCH 17/18] remove redudndant deprecated annotations --- sentry/src/main/java/io/sentry/HubAdapter.java | 1 - sentry/src/main/java/io/sentry/HubScopesWrapper.java | 1 - 2 files changed, 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 42ab6b317e3..5e2d91a9ae8 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -86,7 +86,6 @@ public boolean isEnabled() { return Sentry.captureException(throwable, hint, callback); } - @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { Sentry.captureUserFeedback(userFeedback); diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 271e1084bfb..00395292fd5 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -75,7 +75,6 @@ public boolean isEnabled() { return scopes.captureException(throwable, hint, callback); } - @Deprecated @Override public void captureUserFeedback(@NotNull UserFeedback userFeedback) { scopes.captureUserFeedback(userFeedback); From 7911d0c29a42b0f7d98c7727941d072f87e77d12 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 30 Apr 2026 11:30:00 +0200 Subject: [PATCH 18/18] fix test --- sentry/src/test/java/io/sentry/ScopesAdapterTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt index 43cb5b155a7..1de22cfd3c3 100644 --- a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt @@ -14,6 +14,7 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.reset import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever class ScopesAdapterTest { val scopes: IScopes = mock() @@ -63,6 +64,7 @@ class ScopesAdapterTest { val scopeCallback = mock() val hint = mock() val feedback = Feedback("message") + whenever(scopes.feedback()).thenReturn(FeedbackApi(scopes)) ScopesAdapter.getInstance().captureFeedback(feedback) verify(scopes).captureFeedback(eq(feedback))