From f6940be57d94bd3bdcc05ab34914f112f8c5f946 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 21 Oct 2026 09:32:54 +0200 Subject: [PATCH 1/3] ref(feedback): Remove deprecated user feedback APIs Remove UserFeedback, captureUserFeedback and fromUserFeedback from the core SDK, plus SentryUserFeedbackDialog and the SentryUserFeedbackButton view and composable on Android. All were deprecated in 8.x in favor of Sentry.feedback() and SentryUserFeedbackForm. SentryItemType.UserFeedback stays because hybrid SDKs and envelopes cached by older versions can still carry user_report items. Resolves JAVA-493 Co-Authored-By: Claude Opus 5.5 --- .../api/sentry-android-core.api | 27 --- .../core/SentryUserFeedbackButton.java | 133 ---------- .../core/SentryUserFeedbackDialog.java | 114 --------- .../sentry_oval_button_ripple_background.xml | 10 - .../sentry_user_feedback_button_logo_24.xml | 6 - .../src/main/res/values/attrs.xml | 13 - .../core/SessionTrackingIntegrationTest.kt | 5 - .../uitest/android/UserFeedbackUiTest.kt | 127 ---------- sentry-compose/api/android/sentry-compose.api | 4 - .../compose/SentryUserFeedbackButton.kt | 38 --- .../io/sentry/samples/android/MainActivity.kt | 5 +- .../android/compose/ComposeActivity.kt | 15 +- .../sentry_user_feedback_button_logo_24.xml | 0 sentry/api/sentry.api | 41 ---- .../src/main/java/io/sentry/HubAdapter.java | 5 - .../main/java/io/sentry/HubScopesWrapper.java | 5 - sentry/src/main/java/io/sentry/IScopes.java | 10 - .../main/java/io/sentry/ISentryClient.java | 10 - .../main/java/io/sentry/JsonSerializer.java | 1 - sentry/src/main/java/io/sentry/NoOpHub.java | 4 - .../src/main/java/io/sentry/NoOpScopes.java | 4 - .../main/java/io/sentry/NoOpSentryClient.java | 4 - sentry/src/main/java/io/sentry/Scopes.java | 23 -- .../main/java/io/sentry/ScopesAdapter.java | 6 - sentry/src/main/java/io/sentry/Sentry.java | 13 - .../src/main/java/io/sentry/SentryClient.java | 41 ---- .../java/io/sentry/SentryEnvelopeItem.java | 30 --- .../src/main/java/io/sentry/UserFeedback.java | 229 ------------------ .../src/test/java/io/sentry/HubAdapterTest.kt | 7 - .../test/java/io/sentry/JsonSerializerTest.kt | 35 --- .../io/sentry/JsonUnknownSerializationTest.kt | 4 - .../test/java/io/sentry/ScopesAdapterTest.kt | 7 - sentry/src/test/java/io/sentry/ScopesTest.kt | 48 ---- .../test/java/io/sentry/SentryClientTest.kt | 73 ------ sentry/src/test/java/io/sentry/SentryTest.kt | 13 - .../sentry/UserFeedbackSerializationTest.kt | 81 ------- .../java/io/sentry/UserReportEnvelopeItem.kt | 22 ++ .../sentry/clientreport/ClientReportTest.kt | 7 +- .../io/sentry/transport/RateLimiterTest.kt | 23 +- 39 files changed, 39 insertions(+), 1204 deletions(-) delete mode 100644 sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java delete mode 100644 sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java delete mode 100644 sentry-android-core/src/main/res/drawable/sentry_oval_button_ripple_background.xml delete mode 100644 sentry-android-core/src/main/res/drawable/sentry_user_feedback_button_logo_24.xml delete mode 100644 sentry-android-core/src/main/res/values/attrs.xml delete mode 100644 sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt rename sentry-compose/src/androidMain/res/drawable/sentry_user_feedback_compose_button_logo_24.xml => sentry-samples/sentry-samples-android/src/main/res/drawable/sentry_user_feedback_button_logo_24.xml (100%) delete mode 100644 sentry/src/main/java/io/sentry/UserFeedback.java delete mode 100644 sentry/src/test/java/io/sentry/UserFeedbackSerializationTest.kt create mode 100644 sentry/src/test/java/io/sentry/UserReportEnvelopeItem.kt diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index ae281302482..3e37f1fc398 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -576,33 +576,6 @@ public abstract interface class io/sentry/android/core/SentryShakeDetector$Liste public abstract fun onShake ()V } -public class io/sentry/android/core/SentryUserFeedbackButton : android/widget/Button { - public fun (Landroid/content/Context;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;I)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;II)V - public fun setOnClickListener (Landroid/view/View$OnClickListener;)V -} - -public final class io/sentry/android/core/SentryUserFeedbackDialog : io/sentry/android/core/SentryUserFeedbackForm { -} - -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 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 public fun onDetachedFromWindow ()V 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 deleted file mode 100644 index 589a9fa2ce6..00000000000 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java +++ /dev/null @@ -1,133 +0,0 @@ -package io.sentry.android.core; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.TypedArray; -import android.os.Build; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.widget.Button; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @deprecated `SentryUserFeedbackButton` will be removed in the next major version - */ -// temp fix for JDK 21: Constructors call overridable methods; making them final would break the -// public API. -@SuppressWarnings("this-escape") -@Deprecated -public class SentryUserFeedbackButton extends Button { - - private @Nullable OnClickListener delegate; - - @Deprecated - public SentryUserFeedbackButton(Context context) { - super(context); - init(context, null, 0, 0); - } - - @Deprecated - public SentryUserFeedbackButton(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs, 0, 0); - } - - @Deprecated - public SentryUserFeedbackButton(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context, attrs, defStyleAttr, 0); - } - - @Deprecated - public SentryUserFeedbackButton( - Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(context, attrs, defStyleAttr, defStyleRes); - } - - @SuppressLint("SetTextI18n") - @SuppressWarnings("deprecation") - private void init( - final @NotNull Context context, - final @Nullable AttributeSet attrs, - final int defStyleAttr, - final int defStyleRes) { - try (final @NotNull TypedArray typedArray = - context.obtainStyledAttributes( - attrs, R.styleable.SentryUserFeedbackButton, defStyleAttr, defStyleRes)) { - final float dimensionScale = context.getResources().getDisplayMetrics().density; - final float drawablePadding = - typedArray.getDimension(R.styleable.SentryUserFeedbackButton_android_drawablePadding, -1); - final int drawableStart = - typedArray.getResourceId(R.styleable.SentryUserFeedbackButton_android_drawableStart, -1); - final boolean textAllCaps = - typedArray.getBoolean(R.styleable.SentryUserFeedbackButton_android_textAllCaps, false); - final int background = - typedArray.getResourceId(R.styleable.SentryUserFeedbackButton_android_background, -1); - final float padding = - typedArray.getDimension(R.styleable.SentryUserFeedbackButton_android_padding, -1); - final int textColor = - typedArray.getColor(R.styleable.SentryUserFeedbackButton_android_textColor, -1); - final @Nullable String text = - typedArray.getString(R.styleable.SentryUserFeedbackButton_android_text); - - // If the drawable padding is not set, set it to 4dp - if (drawablePadding == -1) { - setCompoundDrawablePadding((int) (4 * dimensionScale)); - } - - // If the drawable start is not set, set it to the default drawable - if (drawableStart == -1) { - setCompoundDrawablesRelativeWithIntrinsicBounds( - R.drawable.sentry_user_feedback_button_logo_24, 0, 0, 0); - } - - // Set the text all caps - setAllCaps(textAllCaps); - - // If the background is not set, set it to the default background - if (background == -1) { - setBackgroundResource(R.drawable.sentry_oval_button_ripple_background); - } - - // If the padding is not set, set it to 12dp - if (padding == -1) { - int defaultPadding = (int) (12 * dimensionScale); - setPadding(defaultPadding, defaultPadding, defaultPadding, defaultPadding); - } - - // If the text color is not set, set it to the default text color - if (textColor == -1) { - // We need the TypedValue to resolve the color from the theme - final @NotNull TypedValue typedValue = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorForeground, typedValue, true); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - setTextColor(context.getResources().getColor(typedValue.resourceId, context.getTheme())); - } else { - setTextColor(context.getResources().getColor(typedValue.resourceId)); - } - } - - // If the text is not set, set it to "Report a Bug" - if (text == null) { - setText("Report a Bug"); - } - } - - // Set the default ClickListener to open the SentryUserFeedbackForm - setOnClickListener(delegate); - } - - @Override - public void setOnClickListener(final @Nullable OnClickListener listener) { - delegate = listener; - super.setOnClickListener( - v -> { - 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 deleted file mode 100644 index 155464b7b73..00000000000 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.sentry.android.core; - -import android.content.Context; -import io.sentry.SentryFeedbackOptions; -import io.sentry.protocol.SentryId; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @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 SentryUserFeedbackForm.OptionsConfiguration configuration, - final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - super(context, themeResId, associatedEventId, configuration, configurator); - } - - /** - * @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. - * - * @param context the parent context - * @deprecated Use {@link SentryUserFeedbackForm.Builder#Builder(Context)} instead. - */ - @Deprecated - public Builder(final @NotNull Context context) { - super(context); - } - - /** - * Creates a builder for a {@link SentryUserFeedbackDialog} that uses an explicit theme - * resource. - * - * @param context the parent context - * @param themeResId the resource ID of the theme - * @deprecated Use {@link SentryUserFeedbackForm.Builder#Builder(Context, int)} instead. - */ - @Deprecated - public Builder(Context context, int themeResId) { - super(context, themeResId); - } - - /** - * Creates a builder for a {@link SentryUserFeedbackDialog} with a configuration. - * - * @param context the parent context - * @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) { - super(context, configuration); - } - - /** - * 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 - * @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) { - super(context, themeResId, configuration); - } - - @Deprecated - @Override - public Builder configurator( - final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - super.configurator(configurator); - return this; - } - - @Deprecated - @Override - public Builder associatedEventId(final @Nullable SentryId associatedEventId) { - super.associatedEventId(associatedEventId); - return this; - } - - @Deprecated - @Override - public SentryUserFeedbackDialog create() { - return new SentryUserFeedbackDialog( - context, themeResId, associatedEventId, configuration, configurator); - } - } - - /** - * @deprecated Use {@link SentryUserFeedbackForm.OptionsConfiguration} instead. - */ - @Deprecated - public interface OptionsConfiguration extends SentryUserFeedbackForm.OptionsConfiguration {} -} diff --git a/sentry-android-core/src/main/res/drawable/sentry_oval_button_ripple_background.xml b/sentry-android-core/src/main/res/drawable/sentry_oval_button_ripple_background.xml deleted file mode 100644 index 10e58ce27f6..00000000000 --- a/sentry-android-core/src/main/res/drawable/sentry_oval_button_ripple_background.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/sentry-android-core/src/main/res/drawable/sentry_user_feedback_button_logo_24.xml b/sentry-android-core/src/main/res/drawable/sentry_user_feedback_button_logo_24.xml deleted file mode 100644 index 4aab5d6c37b..00000000000 --- a/sentry-android-core/src/main/res/drawable/sentry_user_feedback_button_logo_24.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/sentry-android-core/src/main/res/values/attrs.xml b/sentry-android-core/src/main/res/values/attrs.xml deleted file mode 100644 index efb5651d5da..00000000000 --- a/sentry-android-core/src/main/res/values/attrs.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt index ef920e1d7fe..bbb8783d5fd 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt @@ -23,7 +23,6 @@ import io.sentry.SentryMetricsEvents import io.sentry.SentryReplayEvent import io.sentry.Session import io.sentry.TraceContext -import io.sentry.UserFeedback import io.sentry.protocol.Feedback import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction @@ -156,10 +155,6 @@ class SessionTrackingIntegrationTest { TODO("Not yet implemented") } - override fun captureUserFeedback(userFeedback: UserFeedback) { - TODO("Not yet implemented") - } - override fun captureSession(session: Session, hint: Hint?) { sessionUpdates.add(session) } 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 bfcaf2845b3..791006d3713 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 @@ -1,10 +1,7 @@ package io.sentry.uitest.android -import android.graphics.Color import android.view.View import android.widget.EditText -import android.widget.FrameLayout -import android.widget.LinearLayout import androidx.test.core.app.launchActivity import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click @@ -27,17 +24,14 @@ import io.sentry.SentryItemType 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.SentryUserFeedbackForm import io.sentry.assertEnvelopeFeedback import io.sentry.protocol.SentryId import io.sentry.protocol.User -import io.sentry.test.getProperty import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull -import kotlin.test.assertNull import kotlin.test.assertTrue import org.hamcrest.Description import org.hamcrest.Matcher @@ -522,97 +516,6 @@ class UserFeedbackUiTest : BaseUiTest() { } } - @Test - fun userFeedbackWidgetDefaults() { - initSentry() - var widgetId = 0 - showWidgetAndCheck { widget -> - widgetId = widget.id - val densityScale = context.resources.displayMetrics.density - assertEquals((densityScale * 4).toInt(), widget.compoundDrawablePadding) - - assertNotNull(widget.compoundDrawables[0]) // Drawable left - assertNull(widget.compoundDrawables[1]) // Drawable top - assertNull(widget.compoundDrawables[2]) // Drawable right - assertNull(widget.compoundDrawables[3]) // Drawable bottom - - // Couldn't find a reliable way to check the drawable, so i'll skip it - - assertFalse(widget.isAllCaps) - - assertEquals( - R.drawable.sentry_oval_button_ripple_background, - widget.getProperty("mBackgroundResource"), - ) - - assertEquals((densityScale * 12).toInt(), widget.paddingStart) - assertEquals((densityScale * 12).toInt(), widget.paddingEnd) - assertEquals((densityScale * 12).toInt(), widget.paddingTop) - assertEquals((densityScale * 12).toInt(), widget.paddingBottom) - - assertEquals("Report a Bug", widget.text) - } - - onView(withId(widgetId)).perform(click()) - // Check that the user feedback dialog is shown - checkViewVisibility(R.id.sentry_dialog_user_feedback_layout) - } - - @Test - fun userFeedbackWidgetDefaultsOverridden() { - initSentry() - showWidgetAndCheck({ widget -> - widget.compoundDrawablePadding = 1 - widget.setCompoundDrawables(null, null, null, null) - widget.isAllCaps = true - widget.setBackgroundResource(R.drawable.sentry_edit_text_border) - widget.setTextColor(Color.RED) - widget.text = "My custom text" - widget.setPadding(0, 0, 0, 0) - }) { widget -> - val densityScale = context.resources.displayMetrics.density - assertEquals(1, widget.compoundDrawablePadding) - - assertNull(widget.compoundDrawables[0]) // Drawable left - assertNull(widget.compoundDrawables[1]) // Drawable top - assertNull(widget.compoundDrawables[2]) // Drawable right - assertNull(widget.compoundDrawables[3]) // Drawable bottom - - assertTrue(widget.isAllCaps) - - assertEquals( - R.drawable.sentry_edit_text_border, - widget.getProperty("mBackgroundResource"), - ) - - assertEquals((densityScale * 0).toInt(), widget.paddingStart) - assertEquals((densityScale * 0).toInt(), widget.paddingEnd) - assertEquals((densityScale * 0).toInt(), widget.paddingTop) - assertEquals((densityScale * 0).toInt(), widget.paddingBottom) - - assertEquals(Color.RED, widget.currentTextColor) - - assertEquals("My custom text", widget.text) - } - } - - @Test - fun userFeedbackWidgetShowsDialogOnClickOverridden() { - initSentry() - var widgetId = 0 - var customListenerCalled = false - showWidgetAndCheck { widget -> - widgetId = widget.id - widget.setOnClickListener { customListenerCalled = true } - } - - onView(withId(widgetId)).perform(click()) - // Check that the user feedback dialog is shown - checkViewVisibility(R.id.sentry_dialog_user_feedback_layout) - // And the custom listener is called, too - assertTrue(customListenerCalled) - } - private fun checkViewVisibility(viewId: Int, isGone: Boolean = false) { onView(withId(viewId)) .check(matches(withEffectiveVisibility(if (isGone) Visibility.GONE else Visibility.VISIBLE))) @@ -655,36 +558,6 @@ class UserFeedbackUiTest : BaseUiTest() { checker(dialog) } - private fun showWidgetAndCheck( - widgetConfig: ((widget: SentryUserFeedbackButton) -> Unit)? = null, - checker: (widget: SentryUserFeedbackButton) -> Unit = {}, - ) { - val buttonId = Int.MAX_VALUE - 1 - val feedbackScenario = launchActivity() - feedbackScenario.onActivity { - val layoutParams = - FrameLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT, - ) - val view = - FrameLayout(it).apply { - addView( - SentryUserFeedbackButton(it).apply { - id = buttonId - widgetConfig?.invoke(this) - }, - layoutParams, - ) - } - it.setContentView(view) - } - checkViewVisibility(buttonId) - onView(withId(buttonId)).check(matches(isDisplayed())).check { view, _ -> - checker(view as SentryUserFeedbackButton) - } - } - fun withError(expectedError: String): Matcher { return object : BoundedMatcher(EditText::class.java) { override fun describeTo(description: Description) { diff --git a/sentry-compose/api/android/sentry-compose.api b/sentry-compose/api/android/sentry-compose.api index 356e507c1aa..c74f96163b3 100644 --- a/sentry-compose/api/android/sentry-compose.api +++ b/sentry-compose/api/android/sentry-compose.api @@ -30,10 +30,6 @@ public final class io/sentry/compose/SentryNavigationIntegrationKt { public static final fun withSentryObservableEffect (Landroidx/navigation/NavHostController;ZZLandroidx/compose/runtime/Composer;II)Landroidx/navigation/NavHostController; } -public final class io/sentry/compose/SentryUserFeedbackButtonKt { - public static final fun SentryUserFeedbackButton (Landroidx/compose/ui/Modifier;Ljava/lang/String;Lio/sentry/SentryFeedbackOptions$OptionsConfigurator;Landroidx/compose/runtime/Composer;II)V -} - public final class io/sentry/compose/gestures/ComposeGestureTargetLocator : io/sentry/internal/gestures/GestureTargetLocator { public static final field $stable I public static final field Companion Lio/sentry/compose/gestures/ComposeGestureTargetLocator$Companion; diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt deleted file mode 100644 index d82451b79e2..00000000000 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryUserFeedbackButton.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.sentry.compose - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import io.sentry.Sentry -import io.sentry.SentryFeedbackOptions - -@Deprecated("`SentryUserFeedbackButton` will be removed in the next major version") -@Composable -public fun SentryUserFeedbackButton( - modifier: Modifier = Modifier, - text: String = "Report a Bug", - configurator: SentryFeedbackOptions.OptionsConfigurator? = null, -) { - Button(modifier = modifier, onClick = { Sentry.feedback().show(configurator) }) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - ) { - Icon( - painter = painterResource(id = R.drawable.sentry_user_feedback_compose_button_logo_24), - contentDescription = null, - ) - Spacer(Modifier.padding(horizontal = 4.dp)) - Text(text = text) - } - } -} diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.kt b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.kt index 9660f52ed36..9cfc5b14a78 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.kt +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.kt @@ -775,10 +775,7 @@ fun UserFeedbackScreen() { horizontalArrangement = Arrangement.Center, ) { Icon( - painter = - painterResource( - id = io.sentry.compose.R.drawable.sentry_user_feedback_compose_button_logo_24 - ), + painter = painterResource(id = R.drawable.sentry_user_feedback_button_logo_24), contentDescription = null, ) Spacer(Modifier.padding(horizontal = 4.dp)) diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/compose/ComposeActivity.kt b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/compose/ComposeActivity.kt index 4fd1d74ef7a..e65a1de9eb2 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/compose/ComposeActivity.kt +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/compose/ComposeActivity.kt @@ -49,9 +49,9 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import coil.compose.AsyncImage +import io.sentry.Sentry import io.sentry.android.replay.sentryReplayUnmask import io.sentry.compose.SentryTraced -import io.sentry.compose.SentryUserFeedbackButton import io.sentry.compose.withSentryObservableEffect import io.sentry.samples.android.GitHubApi import io.sentry.samples.android.R as IR @@ -110,9 +110,16 @@ fun Landing(navigateGithub: () -> Unit, navigateGithubWithArgs: () -> Unit) { } } SentryTraced(tag = "button_dialog") { - SentryUserFeedbackButton(modifier = Modifier.padding(top = 32.dp)) { options -> - options.formTitle = "Report a Bug???" - options.messageLabel = "Please provide details about the bug you encountered." + Button( + onClick = { + Sentry.feedback().show { options -> + options.formTitle = "Report a Bug???" + options.messageLabel = "Please provide details about the bug you encountered." + } + }, + modifier = Modifier.padding(top = 32.dp), + ) { + Text("Report a Bug") } } if (showDialog) { diff --git a/sentry-compose/src/androidMain/res/drawable/sentry_user_feedback_compose_button_logo_24.xml b/sentry-samples/sentry-samples-android/src/main/res/drawable/sentry_user_feedback_button_logo_24.xml similarity index 100% rename from sentry-compose/src/androidMain/res/drawable/sentry_user_feedback_compose_button_logo_24.xml rename to sentry-samples/sentry-samples-android/src/main/res/drawable/sentry_user_feedback_button_logo_24.xml diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 71581076594..1ec1a292dc7 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -729,7 +729,6 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun captureProfileChunk (Lio/sentry/ProfileChunk;)Lio/sentry/protocol/SentryId; public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V public fun clone ()Lio/sentry/IHub; public synthetic fun clone ()Ljava/lang/Object; @@ -806,7 +805,6 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public fun captureProfileChunk (Lio/sentry/ProfileChunk;)Lio/sentry/protocol/SentryId; public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V public fun clone ()Lio/sentry/IHub; public synthetic fun clone ()Ljava/lang/Object; @@ -1105,7 +1103,6 @@ public abstract interface class io/sentry/IScopes { public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public abstract fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public abstract fun captureUserFeedback (Lio/sentry/UserFeedback;)V public abstract fun clearBreadcrumbs ()V public abstract fun clone ()Lio/sentry/IHub; public abstract fun close ()V @@ -1206,7 +1203,6 @@ public abstract interface class io/sentry/ISentryClient { public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public abstract fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/IScope;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public abstract fun captureUserFeedback (Lio/sentry/UserFeedback;)V public abstract fun close ()V public abstract fun close (Z)V public abstract fun flush (J)V @@ -1728,7 +1724,6 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun captureProfileChunk (Lio/sentry/ProfileChunk;)Lio/sentry/protocol/SentryId; public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V public fun clone ()Lio/sentry/IHub; public synthetic fun clone ()Ljava/lang/Object; @@ -1923,7 +1918,6 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun captureProfileChunk (Lio/sentry/ProfileChunk;)Lio/sentry/protocol/SentryId; public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V public fun clone ()Lio/sentry/IHub; public synthetic fun clone ()Ljava/lang/Object; @@ -2674,7 +2668,6 @@ public final class io/sentry/Scopes : io/sentry/IScopes { public fun captureProfileChunk (Lio/sentry/ProfileChunk;)Lio/sentry/protocol/SentryId; public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V public fun clone ()Lio/sentry/IHub; public synthetic fun clone ()Ljava/lang/Object; @@ -2754,7 +2747,6 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun captureProfileChunk (Lio/sentry/ProfileChunk;)Lio/sentry/protocol/SentryId; public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V public fun clone ()Lio/sentry/IHub; public synthetic fun clone ()Ljava/lang/Object; @@ -2880,7 +2872,6 @@ public final class io/sentry/Sentry { public static fun captureMessage (Ljava/lang/String;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; public static fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public static fun captureUserFeedback (Lio/sentry/UserFeedback;)V public static fun clearBreadcrumbs ()V public static fun close ()V public static fun configureScope (Lio/sentry/ScopeCallback;)V @@ -3134,7 +3125,6 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient { public fun captureReplayEvent (Lio/sentry/SentryReplayEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureSession (Lio/sentry/Session;Lio/sentry/Hint;)V public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/IScope;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun close ()V public fun close (Z)V public fun flush (J)V @@ -3218,7 +3208,6 @@ public final class io/sentry/SentryEnvelopeItem { public static fun fromProfilingTrace (Lio/sentry/ProfilingTraceData;JLio/sentry/ISerializer;)Lio/sentry/SentryEnvelopeItem; public static fun fromReplay (Lio/sentry/ISerializer;Lio/sentry/ILogger;Lio/sentry/SentryReplayEvent;Lio/sentry/ReplayRecording;Z)Lio/sentry/SentryEnvelopeItem; public static fun fromSession (Lio/sentry/ISerializer;Lio/sentry/Session;)Lio/sentry/SentryEnvelopeItem; - public static fun fromUserFeedback (Lio/sentry/ISerializer;Lio/sentry/UserFeedback;)Lio/sentry/SentryEnvelopeItem; public fun getClientReport (Lio/sentry/ISerializer;)Lio/sentry/clientreport/ClientReport; public fun getData ()[B public fun getEvent (Lio/sentry/ISerializer;)Lio/sentry/SentryEvent; @@ -4914,36 +4903,6 @@ public final class io/sentry/UpdateStatus$UpdateError : io/sentry/UpdateStatus { public fun toString ()Ljava/lang/String; } -public final class io/sentry/UserFeedback : io/sentry/JsonSerializable, io/sentry/JsonUnknown { - public fun (Lio/sentry/protocol/SentryId;)V - public fun (Lio/sentry/protocol/SentryId;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public fun getComments ()Ljava/lang/String; - public fun getEmail ()Ljava/lang/String; - public fun getEventId ()Lio/sentry/protocol/SentryId; - public fun getName ()Ljava/lang/String; - public fun getUnknown ()Ljava/util/Map; - public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V - public fun setComments (Ljava/lang/String;)V - public fun setEmail (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;)V - public fun setUnknown (Ljava/util/Map;)V - public fun toString ()Ljava/lang/String; -} - -public final class io/sentry/UserFeedback$Deserializer : io/sentry/JsonDeserializer { - public fun ()V - public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/UserFeedback; - public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; -} - -public final class io/sentry/UserFeedback$JsonKeys { - public static final field COMMENTS Ljava/lang/String; - public static final field EMAIL Ljava/lang/String; - public static final field EVENT_ID Ljava/lang/String; - public static final field NAME Ljava/lang/String; - public fun ()V -} - public final class io/sentry/W3CTraceparentHeader { public static final field TRACEPARENT_HEADER Ljava/lang/String; public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/Boolean;)V diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 5e2d91a9ae8..ac08df95e98 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -86,11 +86,6 @@ public boolean isEnabled() { return Sentry.captureException(throwable, hint, callback); } - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) { - Sentry.captureUserFeedback(userFeedback); - } - @Override public void startSession() { Sentry.startSession(); diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 00395292fd5..017a6f61d40 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -75,11 +75,6 @@ public boolean isEnabled() { return scopes.captureException(throwable, hint, callback); } - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) { - scopes.captureUserFeedback(userFeedback); - } - @Override public void startSession() { scopes.startSession(); diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index 26ea0dcc3ea..be0aa2b9db3 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -213,16 +213,6 @@ SentryId captureException( final @Nullable Hint hint, final @NotNull ScopeCallback callback); - /** - * 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. */ void startSession(); diff --git a/sentry/src/main/java/io/sentry/ISentryClient.java b/sentry/src/main/java/io/sentry/ISentryClient.java index 2a1df15f812..e646a98977b 100644 --- a/sentry/src/main/java/io/sentry/ISentryClient.java +++ b/sentry/src/main/java/io/sentry/ISentryClient.java @@ -170,16 +170,6 @@ public interface ISentryClient { SentryId captureReplayEvent( @NotNull SentryReplayEvent event, @Nullable IScope scope, @Nullable Hint hint); - /** - * 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); - /** * Captures a session. This method transform a session to an envelope and forwards to * captureEnvelope diff --git a/sentry/src/main/java/io/sentry/JsonSerializer.java b/sentry/src/main/java/io/sentry/JsonSerializer.java index 79a1c72bef3..5fe8af92f4e 100644 --- a/sentry/src/main/java/io/sentry/JsonSerializer.java +++ b/sentry/src/main/java/io/sentry/JsonSerializer.java @@ -143,7 +143,6 @@ public JsonSerializer(@NotNull SentryOptions options) { deserializersByClass.put(SpanStatus.class, new SpanStatus.Deserializer()); deserializersByClass.put(User.class, new User.Deserializer()); deserializersByClass.put(Geo.class, new Geo.Deserializer()); - deserializersByClass.put(UserFeedback.class, new UserFeedback.Deserializer()); deserializersByClass.put(ClientReport.class, new ClientReport.Deserializer()); deserializersByClass.put(ViewHierarchyNode.class, new ViewHierarchyNode.Deserializer()); deserializersByClass.put(ViewHierarchy.class, new ViewHierarchy.Deserializer()); diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index d6ec5774e2b..ba68a449a7e 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -81,10 +81,6 @@ public boolean isEnabled() { return SentryId.EMPTY_ID; } - @Deprecated - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} - @Override public void startSession() {} diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index b9c650c42a8..811e314d475 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -78,10 +78,6 @@ public boolean isEnabled() { return SentryId.EMPTY_ID; } - @Deprecated - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} - @Override public void startSession() {} diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index ac9ff2344bf..6a3d5d3b84e 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -44,10 +44,6 @@ public void flush(long timeoutMillis) {} return SentryId.EMPTY_ID; } - @Deprecated - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} - @Override public void captureSession(@NotNull Session session, @Nullable Hint hint) {} diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index d3926d56993..caea6fe9600 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -349,29 +349,6 @@ private void assignTraceContext(final @NotNull SentryEvent event) { return sentryId; } - @Deprecated - @Override - public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { - if (!isEnabled()) { - getOptions() - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'captureUserFeedback' call is a no-op."); - } else { - try { - getClient().captureUserFeedback(userFeedback); - } catch (Throwable e) { - getOptions() - .getLogger() - .log( - SentryLevel.ERROR, - "Error while capturing captureUserFeedback: " + userFeedback.toString(), - e); - } - } - } - @Override public void startSession() { if (!isEnabled()) { diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index b697a950501..32facd77c52 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -82,12 +82,6 @@ public boolean isEnabled() { return Sentry.captureException(throwable, hint, callback); } - @Deprecated - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) { - Sentry.captureUserFeedback(userFeedback); - } - @Override public void startSession() { Sentry.startSession(); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 03288ae00ad..89ccb97058d 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -927,19 +927,6 @@ public static void close() { return getCurrentScopes().captureException(throwable, hint, callback); } - /** - * 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); - } - /** * Adds a breadcrumb to the current Scope * diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index c0758a4efd4..9c019df1f58 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -769,47 +769,6 @@ private SentryEvent processFeedbackEvent( return feedbackEvent; } - @Deprecated - @Override - public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { - Objects.requireNonNull(userFeedback, "SentryEvent is required."); - - if (SentryId.EMPTY_ID.equals(userFeedback.getEventId())) { - options.getLogger().log(SentryLevel.WARNING, "Capturing userFeedback without a Sentry Id."); - return; - } - options - .getLogger() - .log(SentryLevel.DEBUG, "Capturing userFeedback: %s", userFeedback.getEventId()); - - try { - final @NotNull SentryEnvelope envelope = buildEnvelope(userFeedback); - sendEnvelope(envelope, null); - } catch (IOException e) { - options - .getLogger() - .log( - SentryLevel.WARNING, - e, - "Capturing user feedback %s failed.", - userFeedback.getEventId()); - } - } - - @Deprecated - private @NotNull SentryEnvelope buildEnvelope(final @NotNull UserFeedback userFeedback) { - final List envelopeItems = new ArrayList<>(); - - final SentryEnvelopeItem userFeedbackItem = - SentryEnvelopeItem.fromUserFeedback(options.getSerializer(), userFeedback); - envelopeItems.add(userFeedbackItem); - - final SentryEnvelopeHeader envelopeHeader = - new SentryEnvelopeHeader(userFeedback.getEventId(), options.getSdkVersion()); - - return new SentryEnvelope(envelopeHeader, envelopeItems); - } - private @NotNull SentryEnvelope buildEnvelope( final @NotNull CheckIn checkIn, final @Nullable TraceContext traceContext) { 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 d1858c02767..3d1552a523d 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -176,36 +176,6 @@ public final class SentryEnvelopeItem { } } - @Deprecated - public static SentryEnvelopeItem fromUserFeedback( - final @NotNull ISerializer serializer, final @NotNull UserFeedback userFeedback) { - Objects.requireNonNull(serializer, "ISerializer is required."); - Objects.requireNonNull(userFeedback, "UserFeedback is required."); - - final CachedItem cachedItem = - new CachedItem( - () -> { - try (final ByteArrayOutputStream stream = new ByteArrayOutputStream(); - final Writer writer = - new BufferedWriter( - new OutputStreamWriter(stream, UTF_8), WRITER_BUFFER_SIZE)) { - serializer.serialize(userFeedback, writer); - return stream.toByteArray(); - } - }); - - SentryEnvelopeItemHeader itemHeader = - new SentryEnvelopeItemHeader( - SentryItemType.UserFeedback, - () -> cachedItem.getBytes().length, - "application/json", - null); - - // avoid method refs on Android due to some issues with older AGP setups - // noinspection Convert2MethodRef - return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes()); - } - public static SentryEnvelopeItem fromCheckIn( final @NotNull ISerializer serializer, final @NotNull CheckIn checkIn) { 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 deleted file mode 100644 index b9d0ade0f9d..00000000000 --- a/sentry/src/main/java/io/sentry/UserFeedback.java +++ /dev/null @@ -1,229 +0,0 @@ -package io.sentry; - -import io.sentry.protocol.SentryId; -import io.sentry.vendor.gson.stream.JsonToken; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * 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; - private @Nullable String name; - private @Nullable String email; - private @Nullable String comments; - - private @Nullable Map unknown; - - /** - * Initializes SentryUserFeedback and sets the required eventId. - * - * @param eventId The eventId of the event to which the user feedback is associated. - */ - public UserFeedback(SentryId eventId) { - this(eventId, null, null, null); - } - - /** - * Initializes SentryUserFeedback and sets the required eventId. - * - * @param eventId The eventId of the event to which the user feedback is associated. - * @param name the name of the user. - * @param email the email of the user. - * @param comments comments of the user about what happened. - */ - public UserFeedback( - SentryId eventId, @Nullable String name, @Nullable String email, @Nullable String comments) { - this.eventId = eventId; - this.name = name; - this.email = email; - this.comments = comments; - } - - /** - * Gets the eventId of the event to which the user feedback is associated. - * - * @return the eventId - */ - public SentryId getEventId() { - return eventId; - } - - /** - * Gets the name of the user. - * - * @return the name. - */ - public @Nullable String getName() { - return name; - } - - /** - * Sets the name of the user. - * - * @param name the name of the user. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Gets the email of the user. - * - * @return the email. - */ - public @Nullable String getEmail() { - return email; - } - - /** - * Sets the email of the user. - * - * @param email the email of the user. - */ - public void setEmail(@Nullable String email) { - this.email = email; - } - - /** - * Gets comments of the user about what happened. - * - * @return the comments - */ - public @Nullable String getComments() { - return comments; - } - - /** - * Sets comments of the user about what happened. - * - * @param comments the comments - */ - public void setComments(@Nullable String comments) { - this.comments = comments; - } - - @Override - public String toString() { - return "UserFeedback{" - + "eventId=" - + eventId - + ", name='" - + name - + '\'' - + ", email='" - + email - + '\'' - + ", comments='" - + comments - + '\'' - + '}'; - } - - // JsonKeys - - public static final class JsonKeys { - public static final String EVENT_ID = "event_id"; - public static final String NAME = "name"; - public static final String EMAIL = "email"; - public static final String COMMENTS = "comments"; - } - - // JsonUnknown - - @Override - public @Nullable Map getUnknown() { - return unknown; - } - - @Override - public void setUnknown(@Nullable Map unknown) { - this.unknown = unknown; - } - - // JsonSerializable - - @Override - public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) - throws IOException { - writer.beginObject(); - writer.name(JsonKeys.EVENT_ID); - eventId.serialize(writer, logger); - if (name != null) { - writer.name(JsonKeys.NAME).value(name); - } - if (email != null) { - writer.name(JsonKeys.EMAIL).value(email); - } - if (comments != null) { - writer.name(JsonKeys.COMMENTS).value(comments); - } - if (unknown != null) { - for (String key : unknown.keySet()) { - Object value = unknown.get(key); - writer.name(key).value(logger, value); - } - } - writer.endObject(); - } - - // JsonDeserializer - - public static final class Deserializer implements JsonDeserializer { - @Override - public @NotNull UserFeedback deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) - throws Exception { - SentryId sentryId = null; - String name = null; - String email = null; - String comments = null; - Map unknown = null; - - reader.beginObject(); - while (reader.peek() == JsonToken.NAME) { - final String nextName = reader.nextName(); - switch (nextName) { - case JsonKeys.EVENT_ID: - sentryId = new SentryId.Deserializer().deserialize(reader, logger); - break; - case JsonKeys.NAME: - name = reader.nextStringOrNull(); - break; - case JsonKeys.EMAIL: - email = reader.nextStringOrNull(); - break; - case JsonKeys.COMMENTS: - comments = reader.nextStringOrNull(); - break; - default: - if (unknown == null) { - unknown = new HashMap<>(); - } - reader.nextUnknown(logger, unknown, nextName); - break; - } - } - reader.endObject(); - - if (sentryId == null) { - String message = "Missing required field \"" + JsonKeys.EVENT_ID + "\""; - Exception exception = new IllegalStateException(message); - logger.log(SentryLevel.ERROR, message, exception); - throw exception; - } - - UserFeedback userFeedback = new UserFeedback(sentryId, name, email, comments); - userFeedback.setUnknown(unknown); - return userFeedback; - } - } -} diff --git a/sentry/src/test/java/io/sentry/HubAdapterTest.kt b/sentry/src/test/java/io/sentry/HubAdapterTest.kt index 0dbb6a43c11..4a51a5b1496 100644 --- a/sentry/src/test/java/io/sentry/HubAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/HubAdapterTest.kt @@ -95,13 +95,6 @@ class HubAdapterTest { verify(scopes).captureException(eq(throwable), eq(hint), eq(scopeCallback)) } - @Test - fun `captureUserFeedback calls Hub`() { - val userFeedback = mock() - HubAdapter.getInstance().captureUserFeedback(userFeedback) - verify(scopes).captureUserFeedback(eq(userFeedback)) - } - @Test fun `captureCheckIn calls Hub`() { val checkIn = mock() diff --git a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt index 95b664e13a9..285351828f8 100644 --- a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt @@ -1404,31 +1404,6 @@ class JsonSerializerTest { assertEquals("test", deserialized?.measurements?.get("test_measurement")?.unit) } - @Test - fun `serializing user feedback`() { - val actual = serializeToString(userFeedback) - - val expected = - "{\"event_id\":\"${userFeedback.eventId}\",\"name\":\"${userFeedback.name}\"," + - "\"email\":\"${userFeedback.email}\",\"comments\":\"${userFeedback.comments}\"}" - - assertEquals(expected, actual) - } - - @Test - fun `deserializing user feedback`() { - val jsonUserFeedback = - "{\"event_id\":\"c2fb8fee2e2b49758bcb67cda0f713c7\"," + - "\"name\":\"John\",\"email\":\"john@me.com\",\"comments\":\"comment\"}" - val actual = - fixture.serializer.deserialize(StringReader(jsonUserFeedback), UserFeedback::class.java) - assertNotNull(actual) - assertEquals(userFeedback.eventId, actual.eventId) - assertEquals(userFeedback.name, actual.name) - assertEquals(userFeedback.email, actual.email) - assertEquals(userFeedback.comments, actual.comments) - } - @Test fun `serialize envelope with item throwing`() { val eventID = SentryId() @@ -1744,16 +1719,6 @@ class JsonSerializerTest { "anr_foreground", ) - private val userFeedback: UserFeedback - get() { - val eventId = SentryId("c2fb8fee2e2b49758bcb67cda0f713c7") - return UserFeedback(eventId).apply { - name = "John" - email = "john@me.com" - comments = "comment" - } - } - private val appStartProfilingOptions = SentryAppStartProfilingOptions().apply { traceSampled = false diff --git a/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt b/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt index 37bf1efef98..6231bd2ecfb 100644 --- a/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt @@ -16,7 +16,6 @@ import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryEnvelopeHeaderSerializationTest import io.sentry.protocol.SentryEnvelopeItemHeaderSerializationTest import io.sentry.protocol.SentryException -import io.sentry.protocol.SentryId import io.sentry.protocol.SentryPackage import io.sentry.protocol.SentryRuntime import io.sentry.protocol.SentrySpan @@ -91,8 +90,6 @@ class JsonUnknownSerializationTest( val spanContext = givenJsonUnknown(SpanContext("c2fb8fee2e2b49758bcb67cda0f713c7")) val traceContext = givenJsonUnknown(TraceContextSerializationTest.Fixture().getSut()) val user = givenJsonUnknown(User()) - val userFeedback = - givenJsonUnknown(UserFeedback(SentryId("c2fb8fee2e2b49758bcb67cda0f713c7"))) val geo = givenJsonUnknown(Geo()) // Same instance for first and second param, so we cann access both JsonUnknown and @@ -140,7 +137,6 @@ class JsonUnknownSerializationTest( arrayOf(spanContext, spanContext, SpanContext.Deserializer()::deserialize), arrayOf(traceContext, traceContext, TraceContext.Deserializer()::deserialize), arrayOf(user, user, User.Deserializer()::deserialize), - arrayOf(userFeedback, userFeedback, UserFeedback.Deserializer()::deserialize), arrayOf(geo, geo, Geo.Deserializer()::deserialize), ) } diff --git a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt index 1de22cfd3c3..8e22d98cf74 100644 --- a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt @@ -95,13 +95,6 @@ class ScopesAdapterTest { verify(scopes).captureException(eq(throwable), eq(hint), eq(scopeCallback)) } - @Test - fun `captureUserFeedback calls Scopes`() { - val userFeedback = mock() - ScopesAdapter.getInstance().captureUserFeedback(userFeedback) - verify(scopes).captureUserFeedback(eq(userFeedback)) - } - @Test fun `captureCheckIn calls Scopes`() { val checkIn = mock() diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index a90cd47f0bc..c7c5f93f4d9 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -787,54 +787,6 @@ class ScopesTest { // endregion - // region captureUserFeedback tests - - @Test - fun `when captureUserFeedback is called it is forwarded to the client`() { - val (sut, mockClient) = getEnabledScopes() - sut.captureUserFeedback(userFeedback) - - verify(mockClient) - .captureUserFeedback( - check { - assertEquals(userFeedback.eventId, it.eventId) - assertEquals(userFeedback.email, it.email) - assertEquals(userFeedback.name, it.name) - assertEquals(userFeedback.comments, it.comments) - } - ) - } - - @Test - fun `when captureUserFeedback is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledScopes() - sut.close() - - sut.captureUserFeedback(userFeedback) - verify(mockClient, never()).captureUserFeedback(any()) - } - - @Test - fun `when captureUserFeedback is called and client throws, don't crash`() { - val (sut, mockClient) = getEnabledScopes() - - whenever(mockClient.captureUserFeedback(any())).doThrow(IllegalArgumentException("")) - - sut.captureUserFeedback(userFeedback) - } - - private val userFeedback: UserFeedback - get() { - val eventId = SentryId("c2fb8fee2e2b49758bcb67cda0f713c7") - return UserFeedback(eventId).apply { - name = "John" - email = "john@me.com" - comments = "comment" - } - } - - // endregion - // region captureCheckIn tests @Test diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 6db8ca86521..926cf2d2884 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -1022,66 +1022,6 @@ class SentryClientTest { verifyAttachmentsInEnvelope(event.eventId) } - @Test - fun `when captureUserFeedback with empty id, envelope is not sent`() { - val sut = fixture.getSut() - - sut.captureUserFeedback(UserFeedback(SentryId.EMPTY_ID)) - - verify(fixture.transport, never()).send(any(), anyOrNull()) - } - - @Test - fun `when captureUserFeedback, envelope is sent`() { - val sut = fixture.getSut() - - sut.captureUserFeedback(userFeedback) - - verify(fixture.transport) - .send( - check { actual -> - assertEquals(userFeedback.eventId, actual.header.eventId) - assertEquals(fixture.sentryOptions.sdkVersion, actual.header.sdkVersion) - - assertEquals(1, actual.items.count()) - val item = actual.items.first() - assertEquals(SentryItemType.UserFeedback, item.header.type) - assertEquals("application/json", item.header.contentType) - - assertEnvelopeItemDataForUserFeedback(item) - } - ) - } - - private fun assertEnvelopeItemDataForUserFeedback(item: SentryEnvelopeItem) { - val stream = ByteArrayOutputStream() - val writer = stream.bufferedWriter(Charset.forName("UTF-8")) - fixture.sentryOptions.serializer.serialize(userFeedback, writer) - val expectedData = stream.toByteArray() - assertTrue(Arrays.equals(expectedData, item.data)) - } - - @Test - fun `when captureUserFeedback and connection throws, log exception`() { - val sut = fixture.getSut() - - val exception = IOException("No connection") - whenever(fixture.transport.send(any())).thenThrow(exception) - - val logger = mock() - fixture.sentryOptions.setLogger(logger) - - sut.captureUserFeedback(userFeedback) - - verify(logger) - .log( - SentryLevel.WARNING, - exception, - "Capturing user feedback %s failed.", - userFeedback.eventId, - ) - } - @Test fun `when captureCheckIn, envelope is sent`() { val sut = fixture.getSut() @@ -4517,19 +4457,6 @@ class SentryClientTest { return Session("dis", User(), "env", release) } - private val userFeedback: UserFeedback - get() { - val eventId = SentryId("c2fb8fee2e2b49758bcb67cda0f713c7") - val userFeedback = UserFeedback(eventId) - userFeedback.apply { - name = "John" - email = "john@me.com" - comments = "comment" - } - - return userFeedback - } - private val checkIn = CheckIn("some_slug", CheckInStatus.OK) internal class CustomTransportGate : ITransportGate { diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 6d6212e487b..22cd03244bc 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -365,19 +365,6 @@ class SentryTest { ) } - @Test - fun `captureUserFeedback gets forwarded to client`() { - initForTest { it.dsn = dsn } - - val client = createSentryClientMock() - Sentry.getCurrentScopes().bindClient(client) - - val userFeedback = UserFeedback(SentryId.EMPTY_ID) - Sentry.captureUserFeedback(userFeedback) - - verify(client).captureUserFeedback(argThat { eventId == userFeedback.eventId }) - } - @Test fun `startTransaction sets operation and description`() { initForTest { diff --git a/sentry/src/test/java/io/sentry/UserFeedbackSerializationTest.kt b/sentry/src/test/java/io/sentry/UserFeedbackSerializationTest.kt deleted file mode 100644 index b46f8d2688b..00000000000 --- a/sentry/src/test/java/io/sentry/UserFeedbackSerializationTest.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.sentry - -import io.sentry.protocol.SentryId -import java.io.StringReader -import java.io.StringWriter -import java.lang.Exception -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.fail -import org.junit.Test -import org.mockito.kotlin.any -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify - -class UserFeedbackSerializationTest { - private class Fixture { - val logger = mock() - - fun getSut(): UserFeedback { - val eventId = SentryId("c2fb8fee2e2b49758bcb67cda0f713c7") - return UserFeedback(eventId).apply { - name = "John" - email = "john@me.com" - comments = "comment" - } - } - } - - private val fixture = Fixture() - - @Test - fun serialize() { - val userFeedback = fixture.getSut() - val actual = serialize(userFeedback) - - val expected = - "{\"event_id\":\"${userFeedback.eventId}\",\"name\":\"${userFeedback.name}\"," + - "\"email\":\"${userFeedback.email}\",\"comments\":\"${userFeedback.comments}\"}" - - assertEquals(expected, actual) - } - - @Test - fun deserialize() { - val userFeedback = fixture.getSut() - val jsonUserFeedback = - "{\"event_id\":\"c2fb8fee2e2b49758bcb67cda0f713c7\"," + - "\"name\":\"John\",\"email\":\"john@me.com\",\"comments\":\"comment\"}" - val reader = JsonObjectReader(StringReader(jsonUserFeedback)) - val actual = UserFeedback.Deserializer().deserialize(reader, fixture.logger) - assertNotNull(actual) - assertEquals(userFeedback.eventId, actual.eventId) - assertEquals(userFeedback.name, actual.name) - assertEquals(userFeedback.email, actual.email) - assertEquals(userFeedback.comments, actual.comments) - } - - @Test - fun `deserializing user feedback with missing required fields`() { - val jsonUserFeedbackWithoutEventId = - "{\"name\":\"John\",\"email\":\"john@me.com\"," + "\"comments\":\"comment\"}" - val reader = JsonObjectReader(StringReader(jsonUserFeedbackWithoutEventId)) - - try { - UserFeedback.Deserializer().deserialize(reader, fixture.logger) - fail() - } catch (exception: Exception) { - verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) - } - } - - // Helper - - private fun serialize(jsonSerializable: JsonSerializable): String { - val wrt = StringWriter() - val jsonWrt = JsonObjectWriter(wrt, 100) - jsonSerializable.serialize(jsonWrt, fixture.logger) - return wrt.toString() - } -} diff --git a/sentry/src/test/java/io/sentry/UserReportEnvelopeItem.kt b/sentry/src/test/java/io/sentry/UserReportEnvelopeItem.kt new file mode 100644 index 00000000000..f563b63b7d5 --- /dev/null +++ b/sentry/src/test/java/io/sentry/UserReportEnvelopeItem.kt @@ -0,0 +1,22 @@ +package io.sentry + +import io.sentry.protocol.SentryId + +/** + * The SDK no longer creates `user_report` items, but hybrid SDKs and envelopes cached by older + * versions can still send them through rate limiting and client reports. + */ +fun userReportEnvelopeItem(): SentryEnvelopeItem { + val data = "{\"event_id\":\"${SentryId()}\"}".toByteArray() + val header = + SentryEnvelopeItemHeader( + SentryItemType.UserFeedback, + data.size, + "application/json", + null, + null, + null, + null, + ) + return SentryEnvelopeItem(header, data) +} diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index e077b25fb3c..3408198b657 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -32,7 +32,6 @@ import io.sentry.Session import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext import io.sentry.UncaughtExceptionHandlerIntegration.UncaughtExceptionHint -import io.sentry.UserFeedback import io.sentry.dsnString import io.sentry.hints.Retryable import io.sentry.protocol.Feedback @@ -40,6 +39,7 @@ import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User import io.sentry.test.initForTest +import io.sentry.userReportEnvelopeItem import io.sentry.util.HintUtils import java.io.File import java.time.LocalDateTime @@ -95,10 +95,7 @@ class ClientReportTest { SentryEnvelopeItem.fromEvent(opts.serializer, SentryTransaction(transaction)), SentryEnvelopeItem.fromEvent(opts.serializer, SentryEvent()), SentryEnvelopeItem.fromSession(opts.serializer, Session("dis", User(), "env", "0.0.1")), - SentryEnvelopeItem.fromUserFeedback( - opts.serializer, - UserFeedback(SentryId(UUID.randomUUID())), - ), + userReportEnvelopeItem(), SentryEnvelopeItem.fromAttachment( opts.serializer, NoOpLogger.getInstance(), diff --git a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt index 00ba9cd9872..721c8202a83 100644 --- a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt +++ b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt @@ -29,7 +29,6 @@ import io.sentry.SentryReplayEvent import io.sentry.SentryTracer import io.sentry.Session import io.sentry.TransactionContext -import io.sentry.UserFeedback import io.sentry.clientreport.DiscardReason import io.sentry.clientreport.IClientReportRecorder import io.sentry.hints.DiskFlushNotification @@ -40,9 +39,9 @@ import io.sentry.protocol.User import io.sentry.test.DeferredExecutorService import io.sentry.test.getProperty import io.sentry.time.TestMonotonicTicker +import io.sentry.userReportEnvelopeItem import io.sentry.util.HintUtils import java.io.File -import java.util.UUID import java.util.concurrent.Future import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.SECONDS @@ -259,15 +258,7 @@ class RateLimiterTest { val rateLimiter = fixture.getSUT() val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) - val userFeedbackItem = - SentryEnvelopeItem.fromUserFeedback( - fixture.serializer, - UserFeedback(SentryId(UUID.randomUUID())).also { - it.comments = "It broke on Android. I don't know why, but this happens." - it.email = "john@me.com" - it.setName("John Me") - }, - ) + val userFeedbackItem = userReportEnvelopeItem() val scopes = mock() whenever(scopes.options).thenReturn(SentryOptions()) val transaction = SentryTracer(TransactionContext("name", "op"), scopes) @@ -342,15 +333,7 @@ class RateLimiterTest { whenever(scopes.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) - val userFeedbackItem = - SentryEnvelopeItem.fromUserFeedback( - fixture.serializer, - UserFeedback(SentryId(UUID.randomUUID())).also { - it.comments = "It broke on Android. I don't know why, but this happens." - it.email = "john@me.com" - it.setName("John Me") - }, - ) + val userFeedbackItem = userReportEnvelopeItem() val transaction = SentryTracer(TransactionContext("name", "op"), scopes) val profileItem = SentryEnvelopeItem.fromProfilingTrace( From b92b2b9b2dd92e5210cd2abf8752482b5a32ed70 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 21 Oct 2026 09:33:14 +0200 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51196c33848..12b0db3f13d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +### Breaking Changes + +- Remove the deprecated user feedback APIs ([#6337](https://github.com/getsentry/sentry-java/pull/6337)) + - `UserFeedback` and `Sentry.captureUserFeedback(...)`: use `Sentry.feedback().capture(...)` with `io.sentry.protocol.Feedback` instead + - Android: `SentryUserFeedbackDialog`: use `SentryUserFeedbackForm` instead + - Android: the `SentryUserFeedbackButton` view and composable: call `Sentry.feedback().show()` from your own button instead + ### Fixes - Report the number of dropped spans in client reports when a batched span envelope item is discarded, instead of a quantity of 1 (e.g. for Flutter span streaming) ([#6265](https://github.com/getsentry/sentry-java/pull/6265)) From 894fa4b5c61c8a26279a1f99dd3278a3ce5bf795 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 22 Oct 2026 13:24:18 +0200 Subject: [PATCH 3/3] ref(feedback): Remove deprecated Sentry.captureFeedback and showUserFeedbackDialog Both were thin wrappers around Sentry.feedback(), deprecated in 8.x. HubAdapter now calls Sentry.feedback().capture(...) directly, like ScopesAdapter. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 2 + sentry/api/sentry.api | 6 -- .../src/main/java/io/sentry/HubAdapter.java | 6 +- sentry/src/main/java/io/sentry/Sentry.java | 67 ------------------- sentry/src/test/java/io/sentry/SentryTest.kt | 6 +- 5 files changed, 8 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b0db3f13d..4db4ef4c060 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Remove the deprecated user feedback APIs ([#6337](https://github.com/getsentry/sentry-java/pull/6337)) - `UserFeedback` and `Sentry.captureUserFeedback(...)`: use `Sentry.feedback().capture(...)` with `io.sentry.protocol.Feedback` instead + - `Sentry.captureFeedback(...)`: use `Sentry.feedback().capture(...)` instead + - `Sentry.showUserFeedbackDialog(...)`: use `Sentry.feedback().show(...)` instead - Android: `SentryUserFeedbackDialog`: use `SentryUserFeedbackForm` instead - Android: the `SentryUserFeedbackButton` view and composable: call `Sentry.feedback().show()` from your own button instead diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 1ec1a292dc7..9ed50ff2ae3 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2865,9 +2865,6 @@ public final class io/sentry/Sentry { public static fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public static fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun captureException (Ljava/lang/Throwable;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public static fun captureFeedback (Lio/sentry/protocol/Feedback;)Lio/sentry/protocol/SentryId; - public static fun captureFeedback (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public static fun captureFeedback (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun captureMessage (Ljava/lang/String;)Lio/sentry/protocol/SentryId; public static fun captureMessage (Ljava/lang/String;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; @@ -2926,9 +2923,6 @@ public final class io/sentry/Sentry { public static fun setTag (Ljava/lang/String;Ljava/lang/String;)V public static fun setTransaction (Ljava/lang/String;)V public static fun setUser (Lio/sentry/protocol/User;)V - 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 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 ac08df95e98..6dc5f848889 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -55,18 +55,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 diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 89ccb97058d..0468aca5edb 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -16,7 +16,6 @@ import io.sentry.logger.ILoggerApi; import io.sentry.metrics.IMetricsApi; import io.sentry.opentelemetry.OpenTelemetryUtil; -import io.sentry.protocol.Feedback; import io.sentry.protocol.SentryId; import io.sentry.protocol.User; import io.sentry.transport.NoOpEnvelopeCache; @@ -844,40 +843,6 @@ public static void close() { return getCurrentScopes().captureMessage(message, level, callback); } - /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#capture(Feedback) capture(feedback)} - * instead. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") - public static @NotNull SentryId captureFeedback(final @NotNull Feedback feedback) { - return feedback().capture(feedback); - } - - /** - * @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 feedback().capture(feedback, hint); - } - - /** - * @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 feedback().capture(feedback, hint, callback); - } - /** * Captures the exception. * @@ -1415,38 +1380,6 @@ public static IFeedbackApi feedback() { return getCurrentScopes().feedback(); } - /** - * @deprecated Use {@link #feedback()}.{@link IFeedbackApi#show() show()} instead. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") - public static void showUserFeedbackDialog() { - feedback().show(); - } - - /** - * @deprecated Use {@link #feedback()}.{@link - * IFeedbackApi#show(SentryFeedbackOptions.OptionsConfigurator) show(configurator)} instead. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") - public static void showUserFeedbackDialog( - 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 showUserFeedbackDialog( - final @Nullable SentryId associatedEventId, - final @Nullable SentryFeedbackOptions.OptionsConfigurator configurator) { - feedback().show(associatedEventId, configurator); - } - /** * Sets an attribute on the scope. * diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 22cd03244bc..4986dfe8a85 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -990,9 +990,9 @@ class SentryTest { val feedback = Feedback("message") val hint = Hint() - Sentry.captureFeedback(feedback) - Sentry.captureFeedback(feedback, hint) - Sentry.captureFeedback(feedback, hint) { it.setTag("testKey", "testValue") } + Sentry.feedback().capture(feedback) + Sentry.feedback().capture(feedback, hint) + Sentry.feedback().capture(feedback, hint) { it.setTag("testKey", "testValue") } verify(client).captureFeedback(eq(feedback), eq(null), anyOrNull()) verify(client)