mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Implement encrypted feedback
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
<activity
|
||||
android:name=".android.CrashReportActivity"
|
||||
android:name=".android.DevReportActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="false"
|
||||
android:finishOnTaskLaunch="true"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
@@ -11,10 +12,11 @@
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextAppearance.AppCompat.Large.Inverse"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/crash_report_title"/>
|
||||
tools:text="@string/crash_report_title"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<RelativeLayout
|
||||
@@ -35,8 +37,8 @@
|
||||
android:layout_marginLeft="@dimen/margin_large"
|
||||
android:layout_marginRight="@dimen/margin_large"
|
||||
android:layout_marginStart="@dimen/margin_large"
|
||||
android:hint="@string/describe_crash"
|
||||
android:inputType="textMultiLine|textCapSentences"/>
|
||||
android:inputType="textMultiLine|textCapSentences"
|
||||
tools:hint="@string/describe_crash"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/user_email"
|
||||
@@ -51,13 +53,24 @@
|
||||
android:inputType="textEmailAddress"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/include_debug_report"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/margin_large"
|
||||
android:layout_marginLeft="@dimen/margin_large"
|
||||
android:layout_marginRight="@dimen/margin_large"
|
||||
android:layout_marginStart="@dimen/margin_large"
|
||||
android:layout_marginTop="@dimen/margin_small"
|
||||
android:text="@string/include_debug_report"/>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_small">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/crash_status"
|
||||
android:id="@+id/report_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
@@ -78,10 +91,11 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true"/>
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/share_crash_report"
|
||||
android:id="@+id/share_dev_report"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
@@ -1,21 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/margin_small"
|
||||
android:textSize="@dimen/text_size_large"/>
|
||||
android:orientation="horizontal">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/include_in_report"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/margin_small"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
tools:text="Crash log entry title"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/margin_medium"/>
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
tools:text="Crash log entry value"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -4,11 +4,13 @@
|
||||
<string name="nav_drawer_close_description">Close the navigation drawer</string>
|
||||
<string name="app_name">Briar</string>
|
||||
<string name="crash_report_title">Briar Crash Report</string>
|
||||
<string name="feedback_title">Feedback</string>
|
||||
<string name="describe_crash">Describe what happened</string>
|
||||
<string name="enter_feedback">Enter your feedback</string>
|
||||
<string name="optional_contact_email">Optional contact email</string>
|
||||
<string name="could_not_load_crash_data">Could not load crash data.</string>
|
||||
<string name="crash_report_saved">Crash report saved. It will be sent the next time you log into Briar.</string>
|
||||
<string name="crash_report_not_saved">Could not save crash report to disk.</string>
|
||||
<string name="include_debug_report">Include debug report?</string>
|
||||
<string name="could_not_load_report_data">Could not load report data.</string>
|
||||
<string name="dev_report_saved">Report saved. It will be sent the next time you log into Briar.</string>
|
||||
<string name="ongoing_notification_title">Signed into Briar</string>
|
||||
<string name="ongoing_notification_text">Touch to show the dashboard.</string>
|
||||
<string name="setup_title">Briar Setup</string>
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="send_feedback"
|
||||
android:title="Send feedback"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -38,7 +38,7 @@ import dagger.Component;
|
||||
})
|
||||
public interface AndroidComponent extends CoreEagerSingletons {
|
||||
|
||||
void inject(CrashReportActivity crashReportActivity);
|
||||
void inject(DevReportActivity devReportActivity);
|
||||
|
||||
void inject(SplashScreenActivity activity);
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ import java.util.logging.Logger;
|
||||
logcatArguments = {"-d", "-v", "time", "*:I"},
|
||||
reportSenderFactoryClasses = {BriarReportSenderFactory.class},
|
||||
mode = ReportingInteractionMode.DIALOG,
|
||||
reportDialogClass = CrashReportActivity.class,
|
||||
resDialogOkToast = R.string.crash_report_saved,
|
||||
reportDialogClass = DevReportActivity.class,
|
||||
resDialogOkToast = R.string.dev_report_saved,
|
||||
deleteOldUnsentReportsOnApplicationStart = false
|
||||
)
|
||||
public class BriarApplication extends Application {
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
package org.briarproject.android;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.acra.ACRA;
|
||||
import org.acra.ACRAConstants;
|
||||
import org.acra.ReportField;
|
||||
import org.acra.collector.CrashReportData;
|
||||
import org.acra.dialog.BaseCrashReportDialog;
|
||||
import org.acra.file.CrashReportPersister;
|
||||
import org.acra.prefs.SharedPreferencesFactory;
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.reporting.DevReporter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public class CrashReportActivity extends BaseCrashReportDialog
|
||||
implements DialogInterface.OnClickListener,
|
||||
DialogInterface.OnCancelListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(CrashReportActivity.class.getName());
|
||||
|
||||
private static final String STATE_REVIEWING = "reviewing";
|
||||
|
||||
private SharedPreferencesFactory sharedPreferencesFactory;
|
||||
private EditText userCommentView = null;
|
||||
private EditText userEmailView = null;
|
||||
private LinearLayout status = null;
|
||||
private View progress = null;
|
||||
|
||||
@Inject
|
||||
protected DevReporter reporter;
|
||||
|
||||
boolean reviewing;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
setContentView(R.layout.activity_crash);
|
||||
|
||||
((BriarApplication) getApplication()).getApplicationComponent()
|
||||
.inject(this);
|
||||
|
||||
sharedPreferencesFactory =
|
||||
new SharedPreferencesFactory(getApplicationContext(),
|
||||
getConfig());
|
||||
|
||||
userCommentView = (EditText) findViewById(R.id.user_comment);
|
||||
userEmailView = (EditText) findViewById(R.id.user_email);
|
||||
status = (LinearLayout) findViewById(R.id.crash_status);
|
||||
progress = findViewById(R.id.progress_wheel);
|
||||
|
||||
findViewById(R.id.share_crash_report).setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
processReport();
|
||||
}
|
||||
});
|
||||
|
||||
final SharedPreferences prefs = sharedPreferencesFactory.create();
|
||||
String userEmail = prefs.getString(ACRA.PREF_USER_EMAIL_ADDRESS, "");
|
||||
userEmailView.setText(userEmail);
|
||||
|
||||
if (state != null)
|
||||
reviewing = state.getBoolean(STATE_REVIEWING, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!reviewing) showDialog();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state) {
|
||||
super.onSaveInstanceState(state);
|
||||
state.putBoolean(STATE_REVIEWING, reviewing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// show home screen, otherwise we are crashing again
|
||||
//Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
//intent.addCategory(Intent.CATEGORY_HOME);
|
||||
//startActivity(intent);
|
||||
closeReport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
dialog.dismiss();
|
||||
} else {
|
||||
dialog.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
closeReport();
|
||||
}
|
||||
|
||||
private void showDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this,
|
||||
R.style.BriarDialogTheme);
|
||||
builder.setTitle(R.string.dialog_title_share_crash_report)
|
||||
.setIcon(R.drawable.ic_warning_black_24dp)
|
||||
.setMessage(R.string.dialog_message_share_crash_report)
|
||||
.setPositiveButton(R.string.dialog_button_ok, this)
|
||||
.setNegativeButton(R.string.cancel_button, this);
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.setOnCancelListener(this);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
status.setVisibility(INVISIBLE);
|
||||
progress.setVisibility(VISIBLE);
|
||||
status.removeAllViews();
|
||||
new AsyncTask<Void, Void, CrashReportData>() {
|
||||
|
||||
@Override
|
||||
protected CrashReportData doInBackground(Void... args) {
|
||||
File reportFile = (File) getIntent().getSerializableExtra(
|
||||
ACRAConstants.EXTRA_REPORT_FILE);
|
||||
final CrashReportPersister persister =
|
||||
new CrashReportPersister();
|
||||
try {
|
||||
return persister.load(reportFile);
|
||||
} catch (IOException e) {
|
||||
LOG.log(WARNING, "Could not load report file", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(CrashReportData crashData) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
if (crashData != null) {
|
||||
for (Entry<ReportField, String> e : crashData.entrySet()) {
|
||||
View v = inflater.inflate(R.layout.list_item_crash,
|
||||
status, false);
|
||||
((TextView) v.findViewById(R.id.title))
|
||||
.setText(e.getKey().toString());
|
||||
((TextView) v.findViewById(R.id.content))
|
||||
.setText(e.getValue());
|
||||
status.addView(v);
|
||||
}
|
||||
} else {
|
||||
View v = inflater.inflate(
|
||||
android.R.layout.simple_list_item_1, status, false);
|
||||
((TextView) v.findViewById(android.R.id.text1))
|
||||
.setText(R.string.could_not_load_crash_data);
|
||||
status.addView(v);
|
||||
}
|
||||
status.setVisibility(VISIBLE);
|
||||
progress.setVisibility(GONE);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void processReport() {
|
||||
// Retrieve user comment
|
||||
final String comment = userCommentView != null ?
|
||||
userCommentView.getText().toString() : "";
|
||||
|
||||
// Store the user email
|
||||
final String userEmail;
|
||||
final SharedPreferences prefs = sharedPreferencesFactory.create();
|
||||
if (userEmailView != null) {
|
||||
userEmail = userEmailView.getText().toString();
|
||||
final SharedPreferences.Editor prefEditor = prefs.edit();
|
||||
prefEditor.putString(ACRA.PREF_USER_EMAIL_ADDRESS, userEmail);
|
||||
prefEditor.commit();
|
||||
} else {
|
||||
userEmail = prefs.getString(ACRA.PREF_USER_EMAIL_ADDRESS, "");
|
||||
}
|
||||
sendCrash(comment, userEmail);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void closeReport() {
|
||||
cancelReports();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
package org.briarproject.android;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.acra.ACRA;
|
||||
import org.acra.ACRAConstants;
|
||||
import org.acra.ReportField;
|
||||
import org.acra.collector.CrashReportData;
|
||||
import org.acra.dialog.BaseCrashReportDialog;
|
||||
import org.acra.file.CrashReportPersister;
|
||||
import org.acra.prefs.SharedPreferencesFactory;
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.util.UserFeedback;
|
||||
import org.briarproject.api.reporting.DevReporter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.acra.ReportField.ANDROID_VERSION;
|
||||
import static org.acra.ReportField.APP_VERSION_CODE;
|
||||
import static org.acra.ReportField.APP_VERSION_NAME;
|
||||
import static org.acra.ReportField.PACKAGE_NAME;
|
||||
import static org.acra.ReportField.REPORT_ID;
|
||||
import static org.acra.ReportField.STACK_TRACE;
|
||||
|
||||
public class DevReportActivity extends BaseCrashReportDialog
|
||||
implements DialogInterface.OnClickListener,
|
||||
DialogInterface.OnCancelListener,
|
||||
CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(DevReportActivity.class.getName());
|
||||
|
||||
private static final String PREF_EXCLUDED_FIELDS = "excludedReportFields";
|
||||
private static final String STATE_REVIEWING = "reviewing";
|
||||
private static final Set<ReportField> requiredFields = new HashSet<>();
|
||||
|
||||
static {
|
||||
requiredFields.add(REPORT_ID);
|
||||
requiredFields.add(APP_VERSION_CODE);
|
||||
requiredFields.add(APP_VERSION_NAME);
|
||||
requiredFields.add(PACKAGE_NAME);
|
||||
requiredFields.add(ANDROID_VERSION);
|
||||
requiredFields.add(STACK_TRACE);
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected DevReporter reporter;
|
||||
|
||||
private SharedPreferencesFactory sharedPreferencesFactory;
|
||||
private Set<ReportField> excludedFields;
|
||||
private EditText userCommentView = null;
|
||||
private EditText userEmailView = null;
|
||||
private CheckBox includeDebugReport = null;
|
||||
private LinearLayout report = null;
|
||||
private View progress = null;
|
||||
private View share = null;
|
||||
private boolean reviewing = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
setContentView(R.layout.activity_dev_report);
|
||||
|
||||
((BriarApplication) getApplication()).getApplicationComponent()
|
||||
.inject(this);
|
||||
|
||||
sharedPreferencesFactory =
|
||||
new SharedPreferencesFactory(getApplicationContext(),
|
||||
getConfig());
|
||||
|
||||
final SharedPreferences prefs = sharedPreferencesFactory.create();
|
||||
excludedFields = new HashSet<>();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
for (String name : prefs.getStringSet(PREF_EXCLUDED_FIELDS,
|
||||
new HashSet<String>())) {
|
||||
excludedFields.add(ReportField.valueOf(name));
|
||||
}
|
||||
}
|
||||
|
||||
TextView title = (TextView) findViewById(R.id.title);
|
||||
userCommentView = (EditText) findViewById(R.id.user_comment);
|
||||
userEmailView = (EditText) findViewById(R.id.user_email);
|
||||
includeDebugReport = (CheckBox) findViewById(R.id.include_debug_report);
|
||||
report = (LinearLayout) findViewById(R.id.report_content);
|
||||
progress = findViewById(R.id.progress_wheel);
|
||||
share = findViewById(R.id.share_dev_report);
|
||||
|
||||
title.setText(isFeedback() ? R.string.feedback_title :
|
||||
R.string.crash_report_title);
|
||||
userCommentView.setHint(isFeedback() ? R.string.enter_feedback :
|
||||
R.string.describe_crash);
|
||||
|
||||
includeDebugReport.setVisibility(isFeedback() ? VISIBLE : GONE);
|
||||
report.setVisibility(isFeedback() ? GONE : VISIBLE);
|
||||
|
||||
includeDebugReport.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (includeDebugReport.isChecked())
|
||||
refresh();
|
||||
else
|
||||
report.setVisibility(GONE);
|
||||
}
|
||||
});
|
||||
share.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
processReport();
|
||||
}
|
||||
});
|
||||
|
||||
String userEmail = prefs.getString(ACRA.PREF_USER_EMAIL_ADDRESS, "");
|
||||
userEmailView.setText(userEmail);
|
||||
|
||||
if (state != null)
|
||||
reviewing = state.getBoolean(STATE_REVIEWING, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!isFeedback() && !reviewing) showCrashDialog();
|
||||
if (!isFeedback() || includeDebugReport.isChecked()) refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state) {
|
||||
super.onSaveInstanceState(state);
|
||||
state.putBoolean(STATE_REVIEWING, reviewing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
closeReport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
dialog.dismiss();
|
||||
} else {
|
||||
dialog.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
closeReport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
ReportField field = (ReportField) buttonView.getTag();
|
||||
if (field != null) {
|
||||
if (isChecked)
|
||||
excludedFields.remove(field);
|
||||
else
|
||||
excludedFields.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
|
||||
private boolean isFeedback() {
|
||||
return getException() instanceof UserFeedback;
|
||||
}
|
||||
|
||||
private void showCrashDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this,
|
||||
R.style.BriarDialogTheme);
|
||||
builder.setTitle(R.string.dialog_title_share_crash_report)
|
||||
.setIcon(R.drawable.ic_warning_black_24dp)
|
||||
.setMessage(R.string.dialog_message_share_crash_report)
|
||||
.setPositiveButton(R.string.dialog_button_ok, this)
|
||||
.setNegativeButton(R.string.cancel_button, this);
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.setOnCancelListener(this);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
report.setVisibility(INVISIBLE);
|
||||
progress.setVisibility(VISIBLE);
|
||||
report.removeAllViews();
|
||||
new AsyncTask<Void, Void, CrashReportData>() {
|
||||
|
||||
@Override
|
||||
protected CrashReportData doInBackground(Void... args) {
|
||||
File reportFile = (File) getIntent().getSerializableExtra(
|
||||
ACRAConstants.EXTRA_REPORT_FILE);
|
||||
final CrashReportPersister persister =
|
||||
new CrashReportPersister();
|
||||
try {
|
||||
return persister.load(reportFile);
|
||||
} catch (IOException e) {
|
||||
LOG.log(WARNING, "Could not load report file", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(CrashReportData crashData) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
if (crashData != null) {
|
||||
for (Entry<ReportField, String> e : crashData.entrySet()) {
|
||||
ReportField field = e.getKey();
|
||||
boolean required = requiredFields.contains(field);
|
||||
boolean excluded = excludedFields.contains(field);
|
||||
View v = inflater.inflate(R.layout.list_item_crash,
|
||||
report, false);
|
||||
CheckBox cb = (CheckBox) v
|
||||
.findViewById(R.id.include_in_report);
|
||||
cb.setTag(field);
|
||||
cb.setChecked(required || !excluded);
|
||||
cb.setEnabled(!required);
|
||||
cb.setOnCheckedChangeListener(DevReportActivity.this);
|
||||
((TextView) v.findViewById(R.id.title))
|
||||
.setText(e.getKey().toString());
|
||||
((TextView) v.findViewById(R.id.content))
|
||||
.setText(e.getValue());
|
||||
report.addView(v);
|
||||
}
|
||||
} else {
|
||||
View v = inflater.inflate(
|
||||
android.R.layout.simple_list_item_1, report, false);
|
||||
((TextView) v.findViewById(android.R.id.text1))
|
||||
.setText(R.string.could_not_load_report_data);
|
||||
report.addView(v);
|
||||
}
|
||||
report.setVisibility(VISIBLE);
|
||||
progress.setVisibility(GONE);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void processReport() {
|
||||
userCommentView.setEnabled(false);
|
||||
userEmailView.setEnabled(false);
|
||||
share.setEnabled(false);
|
||||
progress.setVisibility(VISIBLE);
|
||||
final boolean includeReport =
|
||||
!isFeedback() || includeDebugReport.isChecked();
|
||||
new AsyncTask<Void, Void, Boolean>() {
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... args) {
|
||||
File reportFile = (File) getIntent().getSerializableExtra(
|
||||
ACRAConstants.EXTRA_REPORT_FILE);
|
||||
CrashReportPersister persister = new CrashReportPersister();
|
||||
try {
|
||||
CrashReportData data = persister.load(reportFile);
|
||||
if (includeReport) {
|
||||
for (ReportField field : excludedFields) {
|
||||
LOG.info("Removing field " + field.name());
|
||||
data.remove(field);
|
||||
}
|
||||
} else {
|
||||
Iterator<Entry<ReportField, String>> iter =
|
||||
data.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<ReportField, String> e = iter.next();
|
||||
if (!requiredFields.contains(e.getKey())) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
persister.store(data, reportFile);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
LOG.log(WARNING, "Error processing report file", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean success) {
|
||||
final SharedPreferences prefs =
|
||||
sharedPreferencesFactory.create();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
final SharedPreferences.Editor prefEditor =
|
||||
prefs.edit();
|
||||
Set<String> fields = new HashSet<>();
|
||||
for (ReportField field : excludedFields) {
|
||||
fields.add(field.name());
|
||||
}
|
||||
prefEditor.putStringSet(PREF_EXCLUDED_FIELDS, fields);
|
||||
prefEditor.commit();
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Retrieve user comment
|
||||
final String comment = userCommentView != null ?
|
||||
userCommentView.getText().toString() : "";
|
||||
|
||||
// Store the user email
|
||||
final String userEmail;
|
||||
if (userEmailView != null) {
|
||||
userEmail = userEmailView.getText().toString();
|
||||
final SharedPreferences.Editor prefEditor =
|
||||
prefs.edit();
|
||||
prefEditor
|
||||
.putString(ACRA.PREF_USER_EMAIL_ADDRESS,
|
||||
userEmail);
|
||||
prefEditor.commit();
|
||||
} else {
|
||||
userEmail =
|
||||
prefs.getString(ACRA.PREF_USER_EMAIL_ADDRESS,
|
||||
"");
|
||||
}
|
||||
sendCrash(comment, userEmail);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void closeReport() {
|
||||
cancelReports();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,11 @@ import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.acra.ACRA;
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.SettingsActivity;
|
||||
import org.briarproject.android.util.AndroidUtils;
|
||||
import org.briarproject.android.util.UserFeedback;
|
||||
import org.briarproject.android.widget.PreferenceDividerDecoration;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.Event;
|
||||
@@ -131,6 +133,14 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
|
||||
if (SHOW_TESTING_ACTIVITY) {
|
||||
addPreferencesFromResource(R.xml.settings_debug);
|
||||
findPreference("send_feedback").setOnPreferenceClickListener(
|
||||
new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
triggerFeedback();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
@@ -211,6 +221,16 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
});
|
||||
}
|
||||
|
||||
private void triggerFeedback() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ACRA.getErrorReporter()
|
||||
.handleException(new UserFeedback(), false);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||
if (preference == enableBluetooth) {
|
||||
|
||||
@@ -39,7 +39,7 @@ public class BriarReportSender implements ReportSender {
|
||||
throw new ReportSenderException("Couldn't create JSON", e);
|
||||
}
|
||||
try {
|
||||
reporter.encryptCrashReportToFile(
|
||||
reporter.encryptReportToFile(
|
||||
AndroidUtils.getReportDir(context),
|
||||
errorContent.getProperty(ReportField.REPORT_ID),
|
||||
crashReport);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
public class UserFeedback extends Exception {
|
||||
|
||||
}
|
||||
@@ -365,7 +365,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
|
||||
ioExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
reporter.sendCrashReports(
|
||||
reporter.sendReports(
|
||||
AndroidUtils.getReportDir(appContext), SOCKS_PORT);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.api.reporting;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A task for reporting back to the developers.
|
||||
@@ -9,20 +10,20 @@ import java.io.FileNotFoundException;
|
||||
public interface DevReporter {
|
||||
|
||||
/**
|
||||
* Store a crash report encrypted on-disk to be sent later.
|
||||
* Store a report encrypted on-disk to be sent later.
|
||||
*
|
||||
* @param crashReportDir the directory where crash reports are stored.
|
||||
* @param crashReport the crash report in the form expected by the server.
|
||||
* @param reportDir the directory where reports are stored.
|
||||
* @param report the report in the form expected by the server.
|
||||
* @throws FileNotFoundException if the report could not be written.
|
||||
*/
|
||||
void encryptCrashReportToFile(File crashReportDir, String filename,
|
||||
String crashReport) throws FileNotFoundException;
|
||||
void encryptReportToFile(File reportDir, String filename,
|
||||
String report) throws FileNotFoundException;
|
||||
|
||||
/**
|
||||
* Send crash reports previously stored on-disk.
|
||||
* Send reports previously stored on-disk.
|
||||
*
|
||||
* @param crashReportDir the directory where crash reports are stored.
|
||||
* @param socksPort the SOCKS port of a Tor client.
|
||||
* @param reportDir the directory where reports are stored.
|
||||
* @param socksPort the SOCKS port of a Tor client.
|
||||
*/
|
||||
void sendCrashReports(File crashReportDir, int socksPort);
|
||||
void sendReports(File reportDir, int socksPort);
|
||||
}
|
||||
|
||||
@@ -53,17 +53,17 @@ class DevReporterImpl implements DevReporter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encryptCrashReportToFile(File crashReportDir, String filename,
|
||||
String crashReport) throws FileNotFoundException {
|
||||
public void encryptReportToFile(File reportDir, String filename,
|
||||
String report) throws FileNotFoundException {
|
||||
String encryptedReport =
|
||||
crypto.encryptToKey(devConfig.getDevPublicKey(),
|
||||
StringUtils.toUtf8(crashReport));
|
||||
StringUtils.toUtf8(report));
|
||||
|
||||
File report = new File(crashReportDir, filename);
|
||||
File f = new File(reportDir, filename);
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
writer = new PrintWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(report)));
|
||||
new OutputStreamWriter(new FileOutputStream(f)));
|
||||
writer.append(encryptedReport);
|
||||
writer.flush();
|
||||
} finally {
|
||||
@@ -73,10 +73,10 @@ class DevReporterImpl implements DevReporter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCrashReports(File crashReportDir, int socksPort) {
|
||||
File[] reports = crashReportDir.listFiles();
|
||||
public void sendReports(File reportDir, int socksPort) {
|
||||
File[] reports = reportDir.listFiles();
|
||||
if (reports == null || reports.length == 0)
|
||||
return; // No crash reports to send
|
||||
return; // No reports to send
|
||||
|
||||
LOG.info("Connecting to developers");
|
||||
Socket s;
|
||||
@@ -88,7 +88,7 @@ class DevReporterImpl implements DevReporter {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Sending crash reports to developers");
|
||||
LOG.info("Sending reports to developers");
|
||||
OutputStream output;
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
@@ -106,7 +106,7 @@ class DevReporterImpl implements DevReporter {
|
||||
writer.flush();
|
||||
f.delete();
|
||||
}
|
||||
LOG.info("Crash reports sent");
|
||||
LOG.info("Reports sent");
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, "Connection to developers failed", e);
|
||||
|
||||
Reference in New Issue
Block a user