mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
[android] split crash report screen into two fragments
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
package org.briarproject.briar.android.reporting;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class CrashFragment extends Fragment {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
View v = inflater
|
||||||
|
.inflate(R.layout.fragment_crash, container, false);
|
||||||
|
|
||||||
|
v.findViewById(R.id.acceptButton).setOnClickListener(view ->
|
||||||
|
getDevReportActivity().showReportForm(true));
|
||||||
|
v.findViewById(R.id.declineButton).setOnClickListener(view ->
|
||||||
|
getDevReportActivity().closeReport());
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DevReportActivity getDevReportActivity() {
|
||||||
|
return (DevReportActivity) requireNonNull(getActivity());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,86 +2,32 @@ package org.briarproject.briar.android.reporting;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
import android.support.v7.app.AppCompatDelegate;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.acra.ReportField;
|
|
||||||
import org.acra.collector.CrashReportData;
|
|
||||||
import org.acra.dialog.BaseCrashReportDialog;
|
import org.acra.dialog.BaseCrashReportDialog;
|
||||||
import org.acra.file.CrashReportPersister;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.acra.model.Element;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.Localizer;
|
import org.briarproject.briar.android.Localizer;
|
||||||
import org.briarproject.briar.android.util.UserFeedback;
|
import org.briarproject.briar.android.util.UserFeedback;
|
||||||
import org.json.JSONException;
|
|
||||||
|
|
||||||
import java.io.File;
|
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 static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.view.View.GONE;
|
|
||||||
import static android.view.View.INVISIBLE;
|
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||||
import static android.view.inputmethod.InputMethodManager.SHOW_FORCED;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.acra.ACRAConstants.EXTRA_REPORT_FILE;
|
import static org.acra.ACRAConstants.EXTRA_REPORT_FILE;
|
||||||
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;
|
|
||||||
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
||||||
|
|
||||||
public class DevReportActivity extends BaseCrashReportDialog
|
@MethodsNotNullByDefault
|
||||||
implements CompoundButton.OnCheckedChangeListener {
|
@ParametersNotNullByDefault
|
||||||
|
public class DevReportActivity extends BaseCrashReportDialog {
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(DevReportActivity.class.getName());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AppCompatDelegate delegate;
|
private AppCompatDelegate delegate;
|
||||||
private Set<ReportField> excludedFields = new HashSet<>();
|
|
||||||
private EditText userCommentView = null;
|
|
||||||
private EditText userEmailView = null;
|
|
||||||
private CheckBox includeDebugReport = null;
|
|
||||||
private Button chevron = null;
|
|
||||||
private LinearLayout report = null;
|
|
||||||
private View progress = null;
|
|
||||||
private MenuItem sendReport = null;
|
|
||||||
private boolean reviewing = false;
|
|
||||||
|
|
||||||
private AppCompatDelegate getDelegate() {
|
private AppCompatDelegate getDelegate() {
|
||||||
if (delegate == null) {
|
if (delegate == null) {
|
||||||
@@ -110,68 +56,21 @@ public class DevReportActivity extends BaseCrashReportDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Bundle state) {
|
public void init(@Nullable Bundle state) {
|
||||||
super.init(state);
|
super.init(state);
|
||||||
|
|
||||||
if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
|
if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
|
||||||
|
|
||||||
getDelegate().setContentView(R.layout.activity_dev_report);
|
getDelegate().setContentView(R.layout.activity_dev_report);
|
||||||
|
|
||||||
Toolbar tb = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
getDelegate().setSupportActionBar(tb);
|
getDelegate().setSupportActionBar(toolbar);
|
||||||
|
|
||||||
View requestReport = findViewById(R.id.request_report);
|
String title = getString(isFeedback() ? R.string.feedback_title :
|
||||||
View reportForm = findViewById(R.id.report_form);
|
R.string.crash_report_title);
|
||||||
userCommentView = findViewById(R.id.user_comment);
|
requireNonNull(getDelegate().getSupportActionBar()).setTitle(title);
|
||||||
userEmailView = findViewById(R.id.user_email);
|
|
||||||
includeDebugReport = findViewById(R.id.include_debug_report);
|
|
||||||
chevron = findViewById(R.id.chevron);
|
|
||||||
report = findViewById(R.id.report_content);
|
|
||||||
progress = findViewById(R.id.progress_wheel);
|
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
if (state == null) showReportForm(isFeedback());
|
||||||
getDelegate().getSupportActionBar().setTitle(
|
|
||||||
isFeedback() ? R.string.feedback_title :
|
|
||||||
R.string.crash_report_title);
|
|
||||||
userCommentView.setHint(isFeedback() ? R.string.enter_feedback :
|
|
||||||
R.string.describe_crash);
|
|
||||||
|
|
||||||
if (isFeedback()) {
|
|
||||||
includeDebugReport
|
|
||||||
.setText(getString(R.string.include_debug_report_feedback));
|
|
||||||
reportForm.setVisibility(VISIBLE);
|
|
||||||
requestReport.setVisibility(INVISIBLE);
|
|
||||||
} else {
|
|
||||||
includeDebugReport.setChecked(true);
|
|
||||||
reportForm.setVisibility(INVISIBLE);
|
|
||||||
requestReport.setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
findViewById(R.id.acceptButton).setOnClickListener(v -> {
|
|
||||||
reviewing = true;
|
|
||||||
reportForm.setVisibility(VISIBLE);
|
|
||||||
requestReport.setVisibility(INVISIBLE);
|
|
||||||
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
|
|
||||||
.showSoftInput(userCommentView, SHOW_FORCED);
|
|
||||||
});
|
|
||||||
findViewById(R.id.declineButton).setOnClickListener(v -> closeReport());
|
|
||||||
chevron.setOnClickListener(v -> {
|
|
||||||
boolean show =
|
|
||||||
chevron.getText().equals(getString(R.string.show));
|
|
||||||
if (show) {
|
|
||||||
chevron.setText(R.string.hide);
|
|
||||||
refresh();
|
|
||||||
} else {
|
|
||||||
chevron.setText(R.string.show);
|
|
||||||
report.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (state != null)
|
|
||||||
reviewing = state.getBoolean(STATE_REVIEWING, isFeedback());
|
|
||||||
|
|
||||||
if (!isFeedback() && !reviewing)
|
|
||||||
requestReport.setVisibility(VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -181,47 +80,17 @@ public class DevReportActivity extends BaseCrashReportDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPostCreate(Bundle state) {
|
public void onPostCreate(@Nullable Bundle state) {
|
||||||
super.onPostCreate(state);
|
super.onPostCreate(state);
|
||||||
getDelegate().onPostCreate(state);
|
getDelegate().onPostCreate(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
if (chevron.isSelected()) refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostResume() {
|
protected void onPostResume() {
|
||||||
super.onPostResume();
|
super.onPostResume();
|
||||||
getDelegate().onPostResume();
|
getDelegate().onPostResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
// Inflate the menu items for use in the action bar
|
|
||||||
MenuInflater inflater = getDelegate().getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.dev_report_actions, menu);
|
|
||||||
sendReport = menu.findItem(R.id.action_send_report);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
// Handle presses on the action bar items
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case android.R.id.home:
|
|
||||||
onBackPressed();
|
|
||||||
return true;
|
|
||||||
case R.id.action_send_report:
|
|
||||||
processReport();
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTitleChanged(CharSequence title, int color) {
|
public void onTitleChanged(CharSequence title, int color) {
|
||||||
super.onTitleChanged(title, color);
|
super.onTitleChanged(title, color);
|
||||||
@@ -234,12 +103,6 @@ public class DevReportActivity extends BaseCrashReportDialog
|
|||||||
getDelegate().onConfigurationChanged(newConfig);
|
getDelegate().onConfigurationChanged(newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle state) {
|
|
||||||
super.onSaveInstanceState(state);
|
|
||||||
state.putBoolean(STATE_REVIEWING, reviewing);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
@@ -257,132 +120,33 @@ public class DevReportActivity extends BaseCrashReportDialog
|
|||||||
closeReport();
|
closeReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void sendCrashReport(String comment, String email) {
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
sendCrash(comment, email);
|
||||||
ReportField field = (ReportField) buttonView.getTag();
|
|
||||||
if (field != null) {
|
|
||||||
if (isChecked) excludedFields.remove(field);
|
|
||||||
else excludedFields.add(field);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
|
|
||||||
private boolean isFeedback() {
|
private boolean isFeedback() {
|
||||||
return getException() instanceof UserFeedback;
|
return getException() instanceof UserFeedback;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refresh() {
|
void showReportForm(boolean showReportForm) {
|
||||||
report.setVisibility(INVISIBLE);
|
Fragment f;
|
||||||
progress.setVisibility(VISIBLE);
|
if (showReportForm) {
|
||||||
report.removeAllViews();
|
File file =
|
||||||
new AsyncTask<Void, Void, CrashReportData>() {
|
(File) getIntent().getSerializableExtra(EXTRA_REPORT_FILE);
|
||||||
|
f = ReportFormFragment.newInstance(isFeedback(), file);
|
||||||
@Override
|
requireNonNull(getDelegate().getSupportActionBar()).show();
|
||||||
protected CrashReportData doInBackground(Void... args) {
|
} else {
|
||||||
File reportFile = (File) getIntent().getSerializableExtra(
|
f = new CrashFragment();
|
||||||
EXTRA_REPORT_FILE);
|
requireNonNull(getDelegate().getSupportActionBar()).hide();
|
||||||
CrashReportPersister persister = new CrashReportPersister();
|
}
|
||||||
try {
|
getSupportFragmentManager().beginTransaction()
|
||||||
return persister.load(reportFile);
|
.replace(R.id.fragmentContainer, f)
|
||||||
} catch (IOException | JSONException e) {
|
.commit();
|
||||||
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, Element> e : crashData.entrySet()) {
|
|
||||||
ReportField field = e.getKey();
|
|
||||||
String value = e.getValue().toString()
|
|
||||||
.replaceAll("\\\\n", "\n");
|
|
||||||
boolean required = requiredFields.contains(field);
|
|
||||||
boolean excluded = excludedFields.contains(field);
|
|
||||||
View v = inflater.inflate(R.layout.list_item_crash,
|
|
||||||
report, false);
|
|
||||||
CheckBox cb = v.findViewById(R.id.include_in_report);
|
|
||||||
cb.setTag(field);
|
|
||||||
cb.setChecked(required || !excluded);
|
|
||||||
cb.setEnabled(!required);
|
|
||||||
cb.setOnCheckedChangeListener(DevReportActivity.this);
|
|
||||||
cb.setText(field.toString());
|
|
||||||
TextView content = v.findViewById(R.id.content);
|
|
||||||
content.setText(value);
|
|
||||||
report.addView(v);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
View v = inflater.inflate(
|
|
||||||
android.R.layout.simple_list_item_1, report, false);
|
|
||||||
TextView error = v.findViewById(android.R.id.text1);
|
|
||||||
error.setText(R.string.could_not_load_report_data);
|
|
||||||
report.addView(v);
|
|
||||||
}
|
|
||||||
report.setVisibility(VISIBLE);
|
|
||||||
progress.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processReport() {
|
void closeReport() {
|
||||||
userCommentView.setEnabled(false);
|
|
||||||
userEmailView.setEnabled(false);
|
|
||||||
sendReport.setEnabled(false);
|
|
||||||
progress.setVisibility(VISIBLE);
|
|
||||||
boolean includeReport = !isFeedback() || includeDebugReport.isChecked();
|
|
||||||
new AsyncTask<Void, Void, Boolean>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... args) {
|
|
||||||
File reportFile = (File) getIntent().getSerializableExtra(
|
|
||||||
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, Element>> iter =
|
|
||||||
data.entrySet().iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Entry<ReportField, Element> e = iter.next();
|
|
||||||
if (!requiredFields.contains(e.getKey())) {
|
|
||||||
iter.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
persister.store(data, reportFile);
|
|
||||||
return true;
|
|
||||||
} catch (IOException | JSONException e) {
|
|
||||||
LOG.log(WARNING, "Error processing report file", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean success) {
|
|
||||||
if (success) {
|
|
||||||
// Retrieve user's comment and email address, if any
|
|
||||||
String comment = "";
|
|
||||||
if (userCommentView != null)
|
|
||||||
comment = userCommentView.getText().toString();
|
|
||||||
String email = "";
|
|
||||||
if (userEmailView != null) {
|
|
||||||
email = userEmailView.getText().toString();
|
|
||||||
}
|
|
||||||
sendCrash(comment, email);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeReport() {
|
|
||||||
cancelReports();
|
cancelReports();
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
package org.briarproject.briar.android.reporting;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.acra.ReportField;
|
||||||
|
import org.acra.collector.CrashReportData;
|
||||||
|
import org.acra.file.CrashReportPersister;
|
||||||
|
import org.acra.model.Element;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static android.view.MenuItem.SHOW_AS_ACTION_ALWAYS;
|
||||||
|
import static android.view.View.GONE;
|
||||||
|
import static android.view.View.INVISIBLE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.acra.ACRAConstants.EXTRA_REPORT_FILE;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class ReportFormFragment extends Fragment
|
||||||
|
implements CompoundButton.OnCheckedChangeListener {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ReportFormFragment.class.getName());
|
||||||
|
private static final String IS_FEEDBACK = "isFeedback";
|
||||||
|
private static final Set<ReportField> requiredFields = new HashSet<>();
|
||||||
|
private static final Set<ReportField> excludedFields = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFeedback;
|
||||||
|
private File reportFile;
|
||||||
|
|
||||||
|
private EditText userCommentView;
|
||||||
|
private EditText userEmailView;
|
||||||
|
private CheckBox includeDebugReport;
|
||||||
|
private Button chevron;
|
||||||
|
private LinearLayout report;
|
||||||
|
private View progress;
|
||||||
|
@Nullable
|
||||||
|
private MenuItem sendReport;
|
||||||
|
|
||||||
|
static ReportFormFragment newInstance(boolean isFeedback,
|
||||||
|
File reportFile) {
|
||||||
|
ReportFormFragment f = new ReportFormFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putBoolean(IS_FEEDBACK, isFeedback);
|
||||||
|
args.putSerializable(EXTRA_REPORT_FILE, reportFile);
|
||||||
|
f.setArguments(args);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.fragment_report_form, container,
|
||||||
|
false);
|
||||||
|
|
||||||
|
userCommentView = v.findViewById(R.id.user_comment);
|
||||||
|
userEmailView = v.findViewById(R.id.user_email);
|
||||||
|
includeDebugReport = v.findViewById(R.id.include_debug_report);
|
||||||
|
chevron = v.findViewById(R.id.chevron);
|
||||||
|
report = v.findViewById(R.id.report_content);
|
||||||
|
progress = v.findViewById(R.id.progress_wheel);
|
||||||
|
|
||||||
|
Bundle args = requireNonNull(getArguments());
|
||||||
|
isFeedback = args.getBoolean(IS_FEEDBACK);
|
||||||
|
reportFile =
|
||||||
|
(File) requireNonNull(args.getSerializable(EXTRA_REPORT_FILE));
|
||||||
|
|
||||||
|
if (isFeedback) {
|
||||||
|
includeDebugReport
|
||||||
|
.setText(getString(R.string.include_debug_report_feedback));
|
||||||
|
userCommentView.setHint(R.string.enter_feedback);
|
||||||
|
} else {
|
||||||
|
includeDebugReport.setChecked(true);
|
||||||
|
userCommentView.setHint(R.string.describe_crash);
|
||||||
|
}
|
||||||
|
|
||||||
|
chevron.setOnClickListener(view -> {
|
||||||
|
boolean show = chevron.getText().equals(getString(R.string.show));
|
||||||
|
if (show) {
|
||||||
|
chevron.setText(R.string.hide);
|
||||||
|
refresh();
|
||||||
|
} else {
|
||||||
|
chevron.setText(R.string.show);
|
||||||
|
report.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
if (chevron.isSelected()) refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.dev_report_actions, menu);
|
||||||
|
sendReport = menu.findItem(R.id.action_send_report);
|
||||||
|
// calling setShowAsAction() shouldn't be needed, but for some reason is
|
||||||
|
sendReport.setShowAsAction(SHOW_AS_ACTION_ALWAYS);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_send_report) {
|
||||||
|
processReport();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh() {
|
||||||
|
report.setVisibility(INVISIBLE);
|
||||||
|
progress.setVisibility(VISIBLE);
|
||||||
|
report.removeAllViews();
|
||||||
|
new AsyncTask<Void, Void, CrashReportData>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CrashReportData doInBackground(Void... args) {
|
||||||
|
CrashReportPersister persister = new CrashReportPersister();
|
||||||
|
try {
|
||||||
|
return persister.load(reportFile);
|
||||||
|
} catch (IOException | JSONException 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 (Map.Entry<ReportField, Element> e : crashData.entrySet()) {
|
||||||
|
ReportField field = e.getKey();
|
||||||
|
String value = e.getValue().toString()
|
||||||
|
.replaceAll("\\\\n", "\n");
|
||||||
|
boolean required = requiredFields.contains(field);
|
||||||
|
boolean excluded = excludedFields.contains(field);
|
||||||
|
View v = inflater.inflate(R.layout.list_item_crash,
|
||||||
|
report, false);
|
||||||
|
CheckBox cb = v.findViewById(R.id.include_in_report);
|
||||||
|
cb.setTag(field);
|
||||||
|
cb.setChecked(required || !excluded);
|
||||||
|
cb.setEnabled(!required);
|
||||||
|
cb.setOnCheckedChangeListener(ReportFormFragment.this);
|
||||||
|
cb.setText(field.toString());
|
||||||
|
TextView content = v.findViewById(R.id.content);
|
||||||
|
content.setText(value);
|
||||||
|
report.addView(v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
View v = inflater.inflate(
|
||||||
|
android.R.layout.simple_list_item_1, report, false);
|
||||||
|
TextView error = v.findViewById(android.R.id.text1);
|
||||||
|
error.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);
|
||||||
|
requireNonNull(sendReport).setEnabled(false);
|
||||||
|
progress.setVisibility(VISIBLE);
|
||||||
|
boolean includeReport = !isFeedback || includeDebugReport.isChecked();
|
||||||
|
new AsyncTask<Void, Void, Boolean>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... args) {
|
||||||
|
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<Map.Entry<ReportField, Element>> iter =
|
||||||
|
data.entrySet().iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Map.Entry<ReportField, Element> e = iter.next();
|
||||||
|
if (!requiredFields.contains(e.getKey())) {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
persister.store(data, reportFile);
|
||||||
|
return true;
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
LOG.log(WARNING, "Error processing report file", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean success) {
|
||||||
|
if (success) {
|
||||||
|
// Retrieve user's comment and email address, if any
|
||||||
|
String comment = "";
|
||||||
|
if (userCommentView != null)
|
||||||
|
comment = userCommentView.getText().toString();
|
||||||
|
String email = "";
|
||||||
|
if (userEmailView != null) {
|
||||||
|
email = userEmailView.getText().toString();
|
||||||
|
}
|
||||||
|
getDevReportActivity().sendCrashReport(comment, email);
|
||||||
|
}
|
||||||
|
if (getActivity() != null) getActivity().finish();
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DevReportActivity getDevReportActivity() {
|
||||||
|
return (DevReportActivity) requireNonNull(getActivity());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,244 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<include
|
||||||
android:id="@+id/report_form"
|
android:id="@+id/appBar"
|
||||||
|
layout="@layout/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"/>
|
||||||
android:visibility="visible"
|
|
||||||
tools:context=".android.reporting.DevReportActivity"
|
|
||||||
tools:visibility="invisible">
|
|
||||||
|
|
||||||
<include
|
<FrameLayout
|
||||||
android:id="@+id/appBar"
|
android:id="@+id/fragmentContainer"
|
||||||
layout="@layout/toolbar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
|
||||||
android:id="@+id/user_comment_layout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
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_large"
|
|
||||||
app:hintEnabled="false"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/appBar">
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
|
||||||
android:id="@+id/user_comment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="textMultiLine|textCapSentences"
|
|
||||||
android:maxLines="5"
|
|
||||||
tools:hint="@string/describe_crash"/>
|
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
|
||||||
android:id="@+id/user_email_layout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
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"
|
|
||||||
app:hintEnabled="false"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/user_comment_layout">
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
|
||||||
android:id="@+id/user_email"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/optional_contact_email"
|
|
||||||
android:inputType="textEmailAddress"
|
|
||||||
android:maxLines="1"/>
|
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/include_debug_report"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="@dimen/margin_large"
|
|
||||||
android:layout_marginStart="@dimen/margin_large"
|
|
||||||
android:checked="false"
|
|
||||||
android:text="@string/include_debug_report_crash"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/chevron"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/chevron"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/chevron"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/chevron"
|
|
||||||
style="@style/BriarButtonFlat.Positive"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/show"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/user_email_layout"/>
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:id="@+id/report_scroll"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/include_debug_report">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/report_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingBottom="@dimen/listitem_height_one_line_avatar"
|
|
||||||
android:paddingEnd="@dimen/margin_large"
|
|
||||||
android:paddingStart="@dimen/margin_large"
|
|
||||||
android:paddingTop="@dimen/margin_small"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible"/>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progress_wheel"
|
|
||||||
style="?android:attr/progressBarStyleLarge"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/include_debug_report"
|
|
||||||
tools:visibility="visible"/>
|
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:id="@+id/request_report"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"/>
|
||||||
android:fillViewport="true"
|
|
||||||
android:visibility="invisible"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
</LinearLayout>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="@dimen/margin_large">
|
|
||||||
|
|
||||||
<android.support.v7.widget.AppCompatImageView
|
|
||||||
android:id="@+id/errorIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:src="@drawable/ic_crash"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/crashed"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
app:tint="?attr/colorControlNormal"
|
|
||||||
tools:ignore="ContentDescription"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/crashed"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/briar_crashed"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:textSize="@dimen/text_size_large"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/fault"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/errorIcon"
|
|
||||||
tools:layout_editor_absoluteY="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/fault"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_large"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/not_your_fault"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:textSize="@dimen/text_size_large"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/pleaseSend"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/crashed"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/pleaseSend"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_large"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/please_send_report"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:textSize="@dimen/text_size_large"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/encrypted"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/fault"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/encrypted"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_large"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/report_is_encrypted"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:textSize="@dimen/text_size_large"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/acceptButton"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/pleaseSend"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/declineButton"
|
|
||||||
style="@style/BriarButtonFlat.Negative"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/close"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/acceptButton"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/acceptButton"
|
|
||||||
app:layout_constraintHorizontal_weight="1"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/acceptButton"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/acceptButton"
|
|
||||||
style="@style/BriarButtonFlat.Positive"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_large"
|
|
||||||
android:text="@string/send_report"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_weight="1"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/declineButton"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/encrypted"/>
|
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|||||||
119
briar-android/src/main/res/layout/fragment_crash.xml
Normal file
119
briar-android/src/main/res/layout/fragment_crash.xml
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/request_report"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/margin_large">
|
||||||
|
|
||||||
|
<android.support.v7.widget.AppCompatImageView
|
||||||
|
android:id="@+id/errorIcon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:src="@drawable/ic_crash"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/crashed"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
app:tint="?attr/colorControlNormal"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/crashed"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/briar_crashed"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="@dimen/text_size_large"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/fault"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/errorIcon"
|
||||||
|
tools:layout_editor_absoluteY="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fault"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_large"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/not_your_fault"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="@dimen/text_size_large"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/pleaseSend"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/crashed"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pleaseSend"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_large"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/please_send_report"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="@dimen/text_size_large"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/encrypted"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/fault"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/encrypted"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_large"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/report_is_encrypted"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="@dimen/text_size_large"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/acceptButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/pleaseSend"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/declineButton"
|
||||||
|
style="@style/BriarButtonFlat.Negative"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/close"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/acceptButton"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/acceptButton"
|
||||||
|
app:layout_constraintHorizontal_weight="1"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/acceptButton"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/acceptButton"
|
||||||
|
style="@style/BriarButtonFlat.Positive"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_large"
|
||||||
|
android:text="@string/send_report"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_weight="1"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/declineButton"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/encrypted"/>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
113
briar-android/src/main/res/layout/fragment_report_form.xml
Normal file
113
briar-android/src/main/res/layout/fragment_report_form.xml
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/user_comment_layout"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
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_large"
|
||||||
|
app:hintEnabled="false"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/user_comment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textMultiLine|textCapSentences"
|
||||||
|
android:maxLines="5"
|
||||||
|
tools:hint="@string/describe_crash"/>
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/user_email_layout"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
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"
|
||||||
|
app:hintEnabled="false"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/user_comment_layout">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/user_email"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/optional_contact_email"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:maxLines="1"/>
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/include_debug_report"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/margin_large"
|
||||||
|
android:layout_marginStart="@dimen/margin_large"
|
||||||
|
android:checked="false"
|
||||||
|
android:text="@string/include_debug_report_crash"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/chevron"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/chevron"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/chevron"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/chevron"
|
||||||
|
style="@style/BriarButtonFlat.Positive"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/show"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/user_email_layout"/>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/report_scroll"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/include_debug_report">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/report_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/listitem_height_one_line_avatar"
|
||||||
|
android:paddingEnd="@dimen/margin_large"
|
||||||
|
android:paddingStart="@dimen/margin_large"
|
||||||
|
android:paddingTop="@dimen/margin_small"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_wheel"
|
||||||
|
style="?android:attr/progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/include_debug_report"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user