Skip to content

Commit ded4b06

Browse files
authored
Merge d47f374 into 93c1e3f
2 parents 93c1e3f + d47f374 commit ded4b06

File tree

11 files changed

+946
-8
lines changed

11 files changed

+946
-8
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121

2222
- Hook User Interaction integration into running Activity in case of deferred SDK init ([#4337](https://github.com/getsentry/sentry-java/pull/4337))
2323

24+
### Features
25+
26+
- Add new User Feedback API ([#4286](https://github.com/getsentry/sentry-java/pull/4286))
27+
- We now introduced Sentry.captureFeedback, which supersedes Sentry.captureUserFeedback
28+
2429
### Dependencies
2530

2631
- Bump Gradle from v8.13 to v8.14.0 ([#4360](https://github.com/getsentry/sentry-java/pull/4360))

sentry-android-core/api/sentry-android-core.api

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,13 @@ public final class io/sentry/android/core/SentryPerformanceProvider {
385385
public fun shutdown ()V
386386
}
387387

388+
public final class io/sentry/android/core/SentryUserFeedbackDialog : android/app/AlertDialog {
389+
public fun <init> (Landroid/content/Context;)V
390+
public fun <init> (Landroid/content/Context;I)V
391+
public fun <init> (Landroid/content/Context;ZLandroid/content/DialogInterface$OnCancelListener;)V
392+
public fun setCancelable (Z)V
393+
}
394+
388395
public class io/sentry/android/core/SpanFrameMetricsCollector : io/sentry/IPerformanceContinuousCollector, io/sentry/android/core/internal/util/SentryFrameMetricsCollector$FrameMetricsCollectorListener {
389396
protected final field lock Lio/sentry/util/AutoClosableReentrantLock;
390397
public fun <init> (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/internal/util/SentryFrameMetricsCollector;)V
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package io.sentry.android.core;
2+
3+
import android.app.AlertDialog;
4+
import android.content.Context;
5+
import android.content.res.ColorStateList;
6+
import android.graphics.Color;
7+
import android.os.Bundle;
8+
import android.view.View;
9+
import android.widget.Button;
10+
import android.widget.EditText;
11+
import android.widget.ImageView;
12+
import android.widget.TextView;
13+
import android.widget.Toast;
14+
import io.sentry.Sentry;
15+
import io.sentry.SentryFeedbackOptions;
16+
import io.sentry.protocol.Feedback;
17+
import io.sentry.protocol.SentryId;
18+
import io.sentry.protocol.User;
19+
import org.jetbrains.annotations.NotNull;
20+
import org.jetbrains.annotations.Nullable;
21+
22+
public final class SentryUserFeedbackDialog extends AlertDialog {
23+
24+
private boolean isCancelable = false;
25+
26+
public SentryUserFeedbackDialog(final @NotNull Context context) {
27+
super(context);
28+
isCancelable = false;
29+
}
30+
31+
public SentryUserFeedbackDialog(
32+
final @NotNull Context context,
33+
final boolean cancelable,
34+
@Nullable final OnCancelListener cancelListener) {
35+
super(context, cancelable, cancelListener);
36+
isCancelable = cancelable;
37+
}
38+
39+
public SentryUserFeedbackDialog(final @NotNull Context context, final int themeResId) {
40+
super(context, themeResId);
41+
isCancelable = false;
42+
}
43+
44+
@Override
45+
public void setCancelable(boolean cancelable) {
46+
super.setCancelable(cancelable);
47+
isCancelable = cancelable;
48+
}
49+
50+
@Override
51+
protected void onCreate(Bundle savedInstanceState) {
52+
super.onCreate(savedInstanceState);
53+
setContentView(R.layout.sentry_dialog_user_feedback);
54+
setCancelable(isCancelable);
55+
56+
final @NotNull SentryFeedbackOptions feedbackOptions =
57+
Sentry.getCurrentScopes().getOptions().getFeedbackOptions();
58+
final @NotNull TextView lblTitle = findViewById(R.id.sentry_dialog_user_feedback_title);
59+
final @NotNull ImageView imgLogo = findViewById(R.id.sentry_dialog_user_feedback_logo);
60+
final @NotNull TextView lblName = findViewById(R.id.sentry_dialog_user_feedback_txt_name);
61+
final @NotNull EditText edtName = findViewById(R.id.sentry_dialog_user_feedback_edt_name);
62+
final @NotNull TextView lblEmail = findViewById(R.id.sentry_dialog_user_feedback_txt_email);
63+
final @NotNull EditText edtEmail = findViewById(R.id.sentry_dialog_user_feedback_edt_email);
64+
final @NotNull TextView lblMessage =
65+
findViewById(R.id.sentry_dialog_user_feedback_txt_description);
66+
final @NotNull EditText edtMessage =
67+
findViewById(R.id.sentry_dialog_user_feedback_edt_description);
68+
final @NotNull Button btnSend = findViewById(R.id.sentry_dialog_user_feedback_btn_send);
69+
final @NotNull Button btnCancel = findViewById(R.id.sentry_dialog_user_feedback_btn_cancel);
70+
71+
if (feedbackOptions.isShowBranding()) {
72+
imgLogo.setVisibility(View.VISIBLE);
73+
} else {
74+
imgLogo.setVisibility(View.GONE);
75+
}
76+
77+
if (!feedbackOptions.isShowName() && !feedbackOptions.isNameRequired()) {
78+
lblName.setVisibility(View.GONE);
79+
edtName.setVisibility(View.GONE);
80+
} else {
81+
lblName.setVisibility(View.VISIBLE);
82+
edtName.setVisibility(View.VISIBLE);
83+
lblName.setText(feedbackOptions.getNameLabel());
84+
edtName.setHint(feedbackOptions.getNamePlaceholder());
85+
if (feedbackOptions.isNameRequired()) {
86+
lblName.append(feedbackOptions.getIsRequiredLabel());
87+
}
88+
}
89+
90+
if (!feedbackOptions.isShowEmail() && !feedbackOptions.isEmailRequired()) {
91+
lblEmail.setVisibility(View.GONE);
92+
edtEmail.setVisibility(View.GONE);
93+
} else {
94+
lblEmail.setVisibility(View.VISIBLE);
95+
edtEmail.setVisibility(View.VISIBLE);
96+
lblEmail.setText(feedbackOptions.getEmailLabel());
97+
edtEmail.setHint(feedbackOptions.getEmailPlaceholder());
98+
if (feedbackOptions.isEmailRequired()) {
99+
lblEmail.append(feedbackOptions.getIsRequiredLabel());
100+
}
101+
}
102+
103+
if (feedbackOptions.isUseSentryUser()) {
104+
final @Nullable User user = Sentry.getCurrentScopes().getScope().getUser();
105+
if (user != null) {
106+
edtName.setText(user.getName());
107+
edtEmail.setText(user.getEmail());
108+
}
109+
}
110+
111+
lblMessage.setText(feedbackOptions.getMessageLabel());
112+
edtMessage.setHint(feedbackOptions.getMessagePlaceholder());
113+
lblTitle.setText(feedbackOptions.getFormTitle());
114+
115+
btnSend.setBackgroundTintList(
116+
ColorStateList.valueOf(Color.parseColor(feedbackOptions.getSubmitBackgroundHex())));
117+
btnSend.setTextColor(Color.parseColor(feedbackOptions.getSubmitForegroundHex()));
118+
btnSend.setText(feedbackOptions.getSubmitButtonLabel());
119+
btnSend.setOnClickListener(
120+
v -> {
121+
final @NotNull Feedback feedback = new Feedback(edtMessage.getText().toString());
122+
feedback.setName(edtName.getText().toString());
123+
feedback.setContactEmail(edtEmail.getText().toString());
124+
125+
SentryId id = Sentry.captureFeedback(feedback);
126+
if (!id.equals(SentryId.EMPTY_ID)) {
127+
Toast.makeText(
128+
getContext(), feedbackOptions.getSuccessMessageText(), Toast.LENGTH_SHORT)
129+
.show();
130+
final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitSuccess =
131+
feedbackOptions.getOnSubmitSuccess();
132+
if (onSubmitSuccess != null) {
133+
onSubmitSuccess.call(feedback);
134+
}
135+
} else {
136+
final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitError =
137+
feedbackOptions.getOnSubmitError();
138+
if (onSubmitError != null) {
139+
onSubmitError.call(feedback);
140+
}
141+
}
142+
cancel();
143+
});
144+
145+
btnCancel.setText(feedbackOptions.getCancelButtonLabel());
146+
btnCancel.setOnClickListener(v -> cancel());
147+
148+
final @Nullable Runnable onFormClose = feedbackOptions.getOnFormClose();
149+
if (onFormClose != null) {
150+
setOnDismissListener(dialog -> onFormClose.run());
151+
}
152+
}
153+
154+
@Override
155+
protected void onStart() {
156+
super.onStart();
157+
final @NotNull SentryFeedbackOptions feedbackOptions =
158+
Sentry.getCurrentScopes().getOptions().getFeedbackOptions();
159+
final @Nullable Runnable onFormOpen = feedbackOptions.getOnFormOpen();
160+
if (onFormOpen != null) {
161+
onFormOpen.run();
162+
}
163+
}
164+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<shape xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:shape="rectangle">
4+
5+
<solid android:color="@android:color/transparent" />
6+
<stroke
7+
android:width="1dp"
8+
android:color="#FFAAAAAA" /> <!-- border color -->
9+
<corners android:radius="4dp" />
10+
<padding
11+
android:left="8dp"
12+
android:top="8dp"
13+
android:right="8dp"
14+
android:bottom="8dp" />
15+
</shape>
Binary file not shown.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent"
6+
tools:ignore="HardcodedText"
7+
android:theme="?android:attr/dialogTheme"
8+
android:padding="24dp">
9+
10+
11+
<TextView
12+
android:id="@+id/sentry_dialog_user_feedback_title"
13+
android:layout_width="match_parent"
14+
android:layout_height="wrap_content"
15+
android:text="Report a Bug"
16+
style="?android:attr/windowTitleStyle"
17+
android:textStyle="bold"
18+
android:layout_marginBottom="16dp"
19+
android:layout_alignParentTop="true"
20+
android:layout_alignEnd="@+id/sentry_dialog_user_feedback_logo" />
21+
22+
<ImageView
23+
android:id="@+id/sentry_dialog_user_feedback_logo"
24+
android:layout_width="32dp"
25+
android:layout_height="32dp"
26+
android:layout_alignTop="@+id/sentry_dialog_user_feedback_title"
27+
android:layout_alignBottom="@+id/sentry_dialog_user_feedback_title"
28+
android:layout_alignParentEnd="true"
29+
android:tint="?android:attr/colorForeground"
30+
android:src="@drawable/sentry_logo_dark_400x352"/>
31+
32+
<TextView
33+
android:id="@+id/sentry_dialog_user_feedback_txt_name"
34+
android:layout_width="match_parent"
35+
android:layout_height="wrap_content"
36+
android:text="Name"
37+
android:layout_marginTop="4dp"
38+
android:textStyle="bold"
39+
android:layout_below="@id/sentry_dialog_user_feedback_title" />
40+
41+
<EditText
42+
android:id="@+id/sentry_dialog_user_feedback_edt_name"
43+
android:layout_width="match_parent"
44+
android:layout_height="wrap_content"
45+
android:hint="Your Name"
46+
android:inputType="textPersonName"
47+
android:background="@drawable/edit_text_border"
48+
android:paddingHorizontal="8dp"
49+
android:layout_below="@id/sentry_dialog_user_feedback_txt_name" />
50+
51+
<TextView
52+
android:id="@+id/sentry_dialog_user_feedback_txt_email"
53+
android:layout_width="match_parent"
54+
android:layout_height="wrap_content"
55+
android:text="Email"
56+
android:layout_marginTop="8dp"
57+
android:textStyle="bold"
58+
android:layout_below="@id/sentry_dialog_user_feedback_edt_name" />
59+
60+
<EditText
61+
android:id="@+id/sentry_dialog_user_feedback_edt_email"
62+
android:layout_width="match_parent"
63+
android:layout_height="wrap_content"
64+
android:hint="your.email@example.org"
65+
android:inputType="textEmailAddress"
66+
android:background="@drawable/edit_text_border"
67+
android:paddingHorizontal="8dp"
68+
android:layout_below="@id/sentry_dialog_user_feedback_txt_email" />
69+
70+
<TextView
71+
android:id="@+id/sentry_dialog_user_feedback_txt_description"
72+
android:layout_width="match_parent"
73+
android:layout_height="wrap_content"
74+
android:text="Description (Required)"
75+
android:layout_marginTop="8dp"
76+
android:textStyle="bold"
77+
android:layout_below="@id/sentry_dialog_user_feedback_edt_email" />
78+
79+
<EditText
80+
android:id="@+id/sentry_dialog_user_feedback_edt_description"
81+
android:layout_width="match_parent"
82+
android:layout_height="wrap_content"
83+
android:lines="6"
84+
android:inputType="textMultiLine"
85+
android:gravity="top|start"
86+
android:hint="What's the bug? What did you expect?"
87+
android:background="@drawable/edit_text_border"
88+
android:paddingHorizontal="8dp"
89+
android:layout_below="@id/sentry_dialog_user_feedback_txt_description" />
90+
91+
<Button
92+
android:id="@+id/sentry_dialog_user_feedback_btn_send"
93+
android:layout_width="match_parent"
94+
android:layout_height="wrap_content"
95+
android:backgroundTint="#584AC0"
96+
android:textColor="@android:color/white"
97+
android:layout_marginTop="32dp"
98+
android:text="Send Bug Report"
99+
android:layout_below="@id/sentry_dialog_user_feedback_edt_description" />
100+
101+
<Button
102+
android:id="@+id/sentry_dialog_user_feedback_btn_cancel"
103+
android:layout_width="match_parent"
104+
android:layout_height="wrap_content"
105+
android:layout_marginTop="8dp"
106+
android:backgroundTint="?android:attr/colorBackground"
107+
android:text="Cancel"
108+
android:layout_below="@id/sentry_dialog_user_feedback_btn_send" />
109+
110+
</RelativeLayout>

sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import io.sentry.ISpan;
99
import io.sentry.MeasurementUnit;
1010
import io.sentry.Sentry;
11+
import io.sentry.android.core.SentryUserFeedbackDialog;
1112
import io.sentry.instrumentation.file.SentryFileOutputStream;
12-
import io.sentry.protocol.Feedback;
1313
import io.sentry.protocol.User;
1414
import io.sentry.samples.android.compose.ComposeActivity;
1515
import io.sentry.samples.android.databinding.ActivityMainBinding;
@@ -69,12 +69,7 @@ protected void onCreate(Bundle savedInstanceState) {
6969

7070
binding.sendUserFeedback.setOnClickListener(
7171
view -> {
72-
Feedback feedback =
73-
new Feedback("It broke on Android. I don't know why, but this happens.");
74-
feedback.setContactEmail("john@me.com");
75-
feedback.setName("John Me");
76-
77-
Sentry.captureFeedback(feedback);
72+
new SentryUserFeedbackDialog(this).show();
7873
});
7974

8075
binding.addAttachment.setOnClickListener(
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
<resources>
22

33
<!-- Base application theme. -->
4-
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
4+
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
55
<!-- Customize your theme here. -->
66
<item name="colorPrimary">@color/colorPrimary</item>
77
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
88
<item name="colorAccent">@color/colorAccent</item>
9+
<item name="android:dialogTheme">@style/AppThemeDialog</item>
10+
</style>
11+
12+
<!-- Base application theme. -->
13+
<style name="AppThemeDialog" parent="Theme.AppCompat.DayNight.DarkActionBar">
14+
<!-- Customize your theme here. -->
15+
<item name="android:textColor">@color/colorPrimary</item>
16+
<item name="android:editTextColor">@color/colorPrimaryDark</item>
17+
<item name="android:textColorHint">@color/colorPrimaryDark</item>
18+
<item name="android:colorForeground">@color/colorPrimary</item>
919
</style>
1020

1121
</resources>

0 commit comments

Comments
 (0)