diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..ece37d7282581872f2004d3048462a9c544b5f07 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java @@ -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()); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java index d20ef85737754feae03cf319aedaeaea1ba4ff53..1388016c7b85313b6caa167a435e101f82dddf0e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java @@ -2,86 +2,32 @@ package org.briarproject.briar.android.reporting; import android.content.Context; import android.content.res.Configuration; -import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatDelegate; 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.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.briarproject.briar.android.Localizer; import org.briarproject.briar.android.util.UserFeedback; -import org.json.JSONException; 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.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.inputmethod.InputMethodManager.SHOW_FORCED; -import static java.util.logging.Level.WARNING; +import static java.util.Objects.requireNonNull; 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; -public class DevReportActivity extends BaseCrashReportDialog - implements CompoundButton.OnCheckedChangeListener { - - 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); - } +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class DevReportActivity extends BaseCrashReportDialog { 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() { if (delegate == null) { @@ -110,68 +56,21 @@ public class DevReportActivity extends BaseCrashReportDialog } @Override - public void init(Bundle state) { + public void init(@Nullable Bundle state) { super.init(state); if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE); getDelegate().setContentView(R.layout.activity_dev_report); - Toolbar tb = findViewById(R.id.toolbar); - getDelegate().setSupportActionBar(tb); - - View requestReport = findViewById(R.id.request_report); - View reportForm = findViewById(R.id.report_form); - userCommentView = findViewById(R.id.user_comment); - 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 - 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); - } - }); + Toolbar toolbar = findViewById(R.id.toolbar); + getDelegate().setSupportActionBar(toolbar); - if (state != null) - reviewing = state.getBoolean(STATE_REVIEWING, isFeedback()); + String title = getString(isFeedback() ? R.string.feedback_title : + R.string.crash_report_title); + requireNonNull(getDelegate().getSupportActionBar()).setTitle(title); - if (!isFeedback() && !reviewing) - requestReport.setVisibility(VISIBLE); + if (state == null) showReportForm(isFeedback()); } @Override @@ -181,47 +80,17 @@ public class DevReportActivity extends BaseCrashReportDialog } @Override - public void onPostCreate(Bundle state) { + public void onPostCreate(@Nullable Bundle state) { super.onPostCreate(state); getDelegate().onPostCreate(state); } - @Override - public void onStart() { - super.onStart(); - if (chevron.isSelected()) refresh(); - } - @Override protected void onPostResume() { super.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 public void onTitleChanged(CharSequence title, int color) { super.onTitleChanged(title, color); @@ -234,12 +103,6 @@ public class DevReportActivity extends BaseCrashReportDialog getDelegate().onConfigurationChanged(newConfig); } - @Override - public void onSaveInstanceState(Bundle state) { - super.onSaveInstanceState(state); - state.putBoolean(STATE_REVIEWING, reviewing); - } - @Override public void onStop() { super.onStop(); @@ -257,132 +120,33 @@ public class DevReportActivity extends BaseCrashReportDialog 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); - } + void sendCrashReport(String comment, String email) { + sendCrash(comment, email); } - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") private boolean isFeedback() { return getException() instanceof UserFeedback; } - 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( - EXTRA_REPORT_FILE); - 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 (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() { - 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(); + void showReportForm(boolean showReportForm) { + Fragment f; + if (showReportForm) { + File file = + (File) getIntent().getSerializableExtra(EXTRA_REPORT_FILE); + f = ReportFormFragment.newInstance(isFeedback(), file); + requireNonNull(getDelegate().getSupportActionBar()).show(); + } else { + f = new CrashFragment(); + requireNonNull(getDelegate().getSupportActionBar()).hide(); + } + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragmentContainer, f) + .commit(); } - private void closeReport() { + void closeReport() { cancelReports(); finish(); } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..a90e7201201b8bf52dda1746629604030ad85755 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java @@ -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()); + } + +} diff --git a/briar-android/src/main/res/layout/activity_dev_report.xml b/briar-android/src/main/res/layout/activity_dev_report.xml index 5f7271d556929a1a6a94ee762990c40f9ed0f079..c06205fafcfa7d9013163ec7c31e4f5c56a10df9 100644 --- a/briar-android/src/main/res/layout/activity_dev_report.xml +++ b/briar-android/src/main/res/layout/activity_dev_report.xml @@ -1,244 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout +<LinearLayout 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"> + android:layout_height="match_parent" + android:orientation="vertical"> - <android.support.constraint.ConstraintLayout - android:id="@+id/report_form" + <include + android:id="@+id/appBar" + layout="@layout/toolbar" android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="visible" - tools:context=".android.reporting.DevReportActivity" - tools:visibility="invisible"> + android:layout_height="wrap_content"/> - <include - android:id="@+id/appBar" - 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" + <FrameLayout + android:id="@+id/fragmentContainer" android:layout_width="match_parent" - android:layout_height="match_parent" - android:fillViewport="true" - android:visibility="invisible" - tools:visibility="visible"> - - <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_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> + android:layout_height="match_parent"/> -</FrameLayout> \ No newline at end of file +</LinearLayout> diff --git a/briar-android/src/main/res/layout/fragment_crash.xml b/briar-android/src/main/res/layout/fragment_crash.xml new file mode 100644 index 0000000000000000000000000000000000000000..8dd38ac7769e96e504e814c82387c413b666616a --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_crash.xml @@ -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> \ No newline at end of file diff --git a/briar-android/src/main/res/layout/fragment_report_form.xml b/briar-android/src/main/res/layout/fragment_report_form.xml new file mode 100644 index 0000000000000000000000000000000000000000..b69eea3ae78fd8fa303be4af422de992accba24a --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_report_form.xml @@ -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>