-
-
Notifications
You must be signed in to change notification settings - Fork 454
New User Feedback form #4384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
New User Feedback form #4384
Changes from 35 commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
62e71ff
added Feedback class and extended Contexts with it
stefanosiano c9b4850
Added Sentry.captureFeedback API
stefanosiano 813ff1d
updated changelog
stefanosiano dd2d6a8
added tests
stefanosiano 6428dfa
added scope replay id and screen as url
stefanosiano 144bcca
added feedback as DataCategory for rate limit and client report
stefanosiano 509f992
Merge branch 'refs/heads/main' into feat/new-user-feedback-logic
stefanosiano 3c0a6fb
merged main
stefanosiano 9e1066e
added tests
stefanosiano eadb567
Merge branch 'refs/heads/main' into feat/new-user-feedback-logic
stefanosiano 8feeadc
fixed tests
stefanosiano 986772d
started adding resources for UF widget
stefanosiano e19d122
started SentryFeedbackOptions
stefanosiano 79052ad
Merge branch 'feat/new-user-feedback-logic' into feat/user-feedback-w…
stefanosiano 5a83bb8
added all form options
stefanosiano ee44b18
Merge branch 'main' into feat/new-user-feedback-logic
stefanosiano 212ffbf
merged main
stefanosiano 2bf99b5
Merge branch 'feat/new-user-feedback-logic' into feat/user-feedback-w…
stefanosiano 6eb876c
user feedback dialog now uses dialogTheme
stefanosiano a33ae00
Merge branch 'main' into feat/user-feedback-widget
stefanosiano d47f374
merged main
stefanosiano 1a758a2
removed java options for send button colors. Replaced with theme sett…
stefanosiano 9315f7b
added tests and UI tests
stefanosiano 62cea5e
added comments
stefanosiano 794bd66
added comments
stefanosiano f16ac66
added replay capturing on feedback dialog open
stefanosiano 5388b42
added cancel button ui test
stefanosiano 8aacd36
added cancel button ui test
stefanosiano 8750c8d
Merge branch 'refs/heads/main' into feat/user-feedback-widget
stefanosiano 6d14499
added Feedback.toString() and default values in javadoc of SentryFeed…
stefanosiano fb88c1f
merged main
stefanosiano b5eb64d
skipping replay in test when running on gh
stefanosiano d0ba126
skipping replay in test when running on gh
stefanosiano 202759f
Merge branch 'main' into feat/user-feedback-widget
markushi 7bc01eb
Merge branch 'main' into feat/user-feedback-widget
stefanosiano eebaaf3
removed rtl properties and supportsRtl flag
stefanosiano 3fef7c4
updated changelog
stefanosiano 536d522
Merge branch 'main' into feat/user-feedback-widget
stefanosiano 994789a
renamed edit_text_border.xml to sentry_edit_text_border.xml
stefanosiano e6e37b1
Merge branch 'main' into feat/user-feedback-widget
stefanosiano fbbe36c
updated changelog
stefanosiano File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
225 changes: 225 additions & 0 deletions
225
sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackDialog.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
package io.sentry.android.core; | ||
|
||
import android.app.AlertDialog; | ||
import android.content.Context; | ||
import android.os.Bundle; | ||
import android.view.View; | ||
import android.widget.Button; | ||
import android.widget.EditText; | ||
import android.widget.ImageView; | ||
import android.widget.TextView; | ||
import android.widget.Toast; | ||
import io.sentry.IScopes; | ||
import io.sentry.Sentry; | ||
import io.sentry.SentryFeedbackOptions; | ||
import io.sentry.SentryLevel; | ||
import io.sentry.SentryOptions; | ||
import io.sentry.protocol.Feedback; | ||
import io.sentry.protocol.SentryId; | ||
import io.sentry.protocol.User; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
public final class SentryUserFeedbackDialog extends AlertDialog { | ||
|
||
private boolean isCancelable = false; | ||
private @Nullable SentryId currentReplayId; | ||
private @Nullable OnDismissListener delegate; | ||
|
||
public SentryUserFeedbackDialog(final @NotNull Context context) { | ||
super(context); | ||
} | ||
|
||
public SentryUserFeedbackDialog( | ||
stefanosiano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
final @NotNull Context context, | ||
final boolean cancelable, | ||
@Nullable final OnCancelListener cancelListener) { | ||
super(context, cancelable, cancelListener); | ||
isCancelable = cancelable; | ||
} | ||
|
||
public SentryUserFeedbackDialog(final @NotNull Context context, final int themeResId) { | ||
super(context, themeResId); | ||
} | ||
|
||
@Override | ||
public void setCancelable(boolean cancelable) { | ||
super.setCancelable(cancelable); | ||
isCancelable = cancelable; | ||
} | ||
|
||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
setContentView(R.layout.sentry_dialog_user_feedback); | ||
setCancelable(isCancelable); | ||
|
||
final @NotNull SentryFeedbackOptions feedbackOptions = | ||
Sentry.getCurrentScopes().getOptions().getFeedbackOptions(); | ||
final @NotNull TextView lblTitle = findViewById(R.id.sentry_dialog_user_feedback_title); | ||
final @NotNull ImageView imgLogo = findViewById(R.id.sentry_dialog_user_feedback_logo); | ||
final @NotNull TextView lblName = findViewById(R.id.sentry_dialog_user_feedback_txt_name); | ||
final @NotNull EditText edtName = findViewById(R.id.sentry_dialog_user_feedback_edt_name); | ||
final @NotNull TextView lblEmail = findViewById(R.id.sentry_dialog_user_feedback_txt_email); | ||
final @NotNull EditText edtEmail = findViewById(R.id.sentry_dialog_user_feedback_edt_email); | ||
final @NotNull TextView lblMessage = | ||
findViewById(R.id.sentry_dialog_user_feedback_txt_description); | ||
final @NotNull EditText edtMessage = | ||
findViewById(R.id.sentry_dialog_user_feedback_edt_description); | ||
final @NotNull Button btnSend = findViewById(R.id.sentry_dialog_user_feedback_btn_send); | ||
final @NotNull Button btnCancel = findViewById(R.id.sentry_dialog_user_feedback_btn_cancel); | ||
|
||
if (feedbackOptions.isShowBranding()) { | ||
imgLogo.setVisibility(View.VISIBLE); | ||
} else { | ||
imgLogo.setVisibility(View.GONE); | ||
} | ||
|
||
// If name is required, ignore showName flag | ||
if (!feedbackOptions.isShowName() && !feedbackOptions.isNameRequired()) { | ||
lblName.setVisibility(View.GONE); | ||
edtName.setVisibility(View.GONE); | ||
} else { | ||
lblName.setVisibility(View.VISIBLE); | ||
edtName.setVisibility(View.VISIBLE); | ||
lblName.setText(feedbackOptions.getNameLabel()); | ||
edtName.setHint(feedbackOptions.getNamePlaceholder()); | ||
if (feedbackOptions.isNameRequired()) { | ||
lblName.append(feedbackOptions.getIsRequiredLabel()); | ||
} | ||
} | ||
|
||
// If email is required, ignore showEmail flag | ||
if (!feedbackOptions.isShowEmail() && !feedbackOptions.isEmailRequired()) { | ||
lblEmail.setVisibility(View.GONE); | ||
edtEmail.setVisibility(View.GONE); | ||
} else { | ||
lblEmail.setVisibility(View.VISIBLE); | ||
edtEmail.setVisibility(View.VISIBLE); | ||
lblEmail.setText(feedbackOptions.getEmailLabel()); | ||
edtEmail.setHint(feedbackOptions.getEmailPlaceholder()); | ||
if (feedbackOptions.isEmailRequired()) { | ||
lblEmail.append(feedbackOptions.getIsRequiredLabel()); | ||
} | ||
} | ||
|
||
// If Sentry user is set, and useSentryUser is true, populate the name and email | ||
if (feedbackOptions.isUseSentryUser()) { | ||
final @Nullable User user = Sentry.getCurrentScopes().getScope().getUser(); | ||
if (user != null) { | ||
edtName.setText(user.getName()); | ||
edtEmail.setText(user.getEmail()); | ||
} | ||
} | ||
|
||
lblMessage.setText(feedbackOptions.getMessageLabel()); | ||
lblMessage.append(feedbackOptions.getIsRequiredLabel()); | ||
edtMessage.setHint(feedbackOptions.getMessagePlaceholder()); | ||
lblTitle.setText(feedbackOptions.getFormTitle()); | ||
|
||
btnSend.setText(feedbackOptions.getSubmitButtonLabel()); | ||
btnSend.setOnClickListener( | ||
v -> { | ||
// Gather fields and trim them | ||
final @NotNull String name = edtName.getText().toString().trim(); | ||
final @NotNull String email = edtEmail.getText().toString().trim(); | ||
final @NotNull String message = edtMessage.getText().toString().trim(); | ||
|
||
// If a required field is missing, shows the error label | ||
if (name.isEmpty() && feedbackOptions.isNameRequired()) { | ||
edtName.setError(lblName.getText()); | ||
return; | ||
} | ||
|
||
if (email.isEmpty() && feedbackOptions.isEmailRequired()) { | ||
edtEmail.setError(lblEmail.getText()); | ||
return; | ||
} | ||
|
||
if (message.isEmpty()) { | ||
edtMessage.setError(lblMessage.getText()); | ||
return; | ||
} | ||
|
||
// Create the feedback object | ||
final @NotNull Feedback feedback = new Feedback(message); | ||
feedback.setName(name); | ||
feedback.setContactEmail(email); | ||
if (currentReplayId != null) { | ||
feedback.setReplayId(currentReplayId); | ||
} | ||
|
||
// Capture the feedback. If the ID is empty, it means that the feedback was not sent | ||
final @NotNull SentryId id = Sentry.captureFeedback(feedback); | ||
if (!id.equals(SentryId.EMPTY_ID)) { | ||
Toast.makeText( | ||
getContext(), feedbackOptions.getSuccessMessageText(), Toast.LENGTH_SHORT) | ||
.show(); | ||
final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitSuccess = | ||
feedbackOptions.getOnSubmitSuccess(); | ||
if (onSubmitSuccess != null) { | ||
onSubmitSuccess.call(feedback); | ||
} | ||
} else { | ||
final @Nullable SentryFeedbackOptions.SentryFeedbackCallback onSubmitError = | ||
feedbackOptions.getOnSubmitError(); | ||
if (onSubmitError != null) { | ||
onSubmitError.call(feedback); | ||
} | ||
} | ||
cancel(); | ||
}); | ||
|
||
btnCancel.setText(feedbackOptions.getCancelButtonLabel()); | ||
btnCancel.setOnClickListener(v -> cancel()); | ||
setOnDismissListener(delegate); | ||
} | ||
|
||
@Override | ||
public void setOnDismissListener(final @Nullable OnDismissListener listener) { | ||
delegate = listener; | ||
// If the user set a custom onDismissListener, we ensure it doesn't override the onFormClose | ||
final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); | ||
final @Nullable Runnable onFormClose = options.getFeedbackOptions().getOnFormClose(); | ||
if (onFormClose != null) { | ||
super.setOnDismissListener( | ||
dialog -> { | ||
onFormClose.run(); | ||
currentReplayId = null; | ||
if (delegate != null) { | ||
delegate.onDismiss(dialog); | ||
} | ||
}); | ||
} else { | ||
super.setOnDismissListener(delegate); | ||
} | ||
} | ||
|
||
@Override | ||
protected void onStart() { | ||
super.onStart(); | ||
final @NotNull SentryOptions options = Sentry.getCurrentScopes().getOptions(); | ||
final @NotNull SentryFeedbackOptions feedbackOptions = options.getFeedbackOptions(); | ||
final @Nullable Runnable onFormOpen = feedbackOptions.getOnFormOpen(); | ||
if (onFormOpen != null) { | ||
onFormOpen.run(); | ||
} | ||
options.getReplayController().captureReplay(false); | ||
currentReplayId = options.getReplayController().getReplayId(); | ||
} | ||
|
||
@Override | ||
public void show() { | ||
// If Sentry is disabled, don't show the dialog, but log a warning | ||
final @NotNull IScopes scopes = Sentry.getCurrentScopes(); | ||
final @NotNull SentryOptions options = scopes.getOptions(); | ||
if (!scopes.isEnabled() || !options.isEnabled()) { | ||
options | ||
.getLogger() | ||
.log(SentryLevel.WARNING, "Sentry is disabled. Feedback dialog won't be shown."); | ||
return; | ||
} | ||
// Otherwise, show the dialog | ||
super.show(); | ||
} | ||
} |
stefanosiano marked this conversation as resolved.
Show resolved
Hide resolved
|
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+6.52 KB
sentry-android-core/src/main/res/drawable-xhdpi/sentry_logo_dark.webp
Binary file not shown.
Binary file added
BIN
+11.9 KB
sentry-android-core/src/main/res/drawable-xxhdpi/sentry_logo_dark.webp
Binary file not shown.
Binary file added
BIN
+2.79 KB
sentry-android-core/src/main/res/drawable-xxxhdpi/sentry_logo_dark.webp
Binary file not shown.
15 changes: 15 additions & 0 deletions
15
sentry-android-core/src/main/res/drawable/edit_text_border.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
stefanosiano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<shape xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:shape="rectangle"> | ||
|
||
<solid android:color="@android:color/transparent" /> | ||
<stroke | ||
android:width="1dp" | ||
android:color="#FFAAAAAA" /> <!-- border color --> | ||
<corners android:radius="4dp" /> | ||
<padding | ||
android:left="8dp" | ||
android:top="8dp" | ||
android:right="8dp" | ||
android:bottom="8dp" /> | ||
</shape> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.