From 3de5afc68e2ddf98c26b11c229a457d82a5ae3d0 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 28 Jun 2020 16:07:17 +0200 Subject: [PATCH] Add Markdown export of crash logs Add app language as additional debug information to reports --- .../schabi/newpipe/report/ErrorActivity.java | 176 +++++++++++++----- app/src/main/res/layout/activity_error.xml | 22 ++- app/src/main/res/values-de/strings.xml | 4 +- app/src/main/res/values/strings.xml | 5 +- 4 files changed, 159 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java index 20724c02b..52761e467 100644 --- a/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java @@ -11,7 +11,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; -import android.preference.PreferenceManager; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -20,6 +19,7 @@ import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -37,6 +37,8 @@ import org.schabi.newpipe.ActivityCommunicator; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; +import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ThemeHelper; import java.io.PrintWriter; @@ -45,7 +47,6 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.TimeZone; import java.util.Vector; @@ -81,6 +82,10 @@ public class ErrorActivity extends AppCompatActivity { public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org"; public static final String ERROR_EMAIL_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME; + + public static final String ERROR_GITHUB_ISSUE_URL + = "https://github.com/TeamNewPipe/NewPipe/issues"; + private String[] errorList; private ErrorInfo errorInfo; private Class returnActivity; @@ -193,7 +198,10 @@ public class ErrorActivity extends AppCompatActivity { actionBar.setDisplayShowTitleEnabled(true); } - Button reportButton = findViewById(R.id.errorReportButton); + final Button reportEmailButton = findViewById(R.id.errorReportEmailButton); + final Button copyButton = findViewById(R.id.errorReportCopyButton); + final Button reportGithubButton = findViewById(R.id.errorReportGitHubButton); + userCommentBox = findViewById(R.id.errorCommentBox); TextView errorView = findViewById(R.id.errorView); TextView infoView = findViewById(R.id.errorInfosView); @@ -205,40 +213,23 @@ public class ErrorActivity extends AppCompatActivity { errorList = intent.getStringArrayExtra(ERROR_LIST); // important add guru meditation - addGuruMeditaion(); + addGuruMeditation(); currentTimeStamp = getCurrentTimeStamp(); - reportButton.setOnClickListener((View v) -> { - Context context = this; - new AlertDialog.Builder(context) - .setIcon(android.R.drawable.ic_dialog_alert) - .setTitle(R.string.privacy_policy_title) - .setMessage(R.string.start_accept_privacy_policy) - .setCancelable(false) - .setNeutralButton(R.string.read_privacy_policy, (dialog, which) -> { - Intent webIntent = new Intent(Intent.ACTION_VIEW, - Uri.parse(context.getString(R.string.privacy_policy_url)) - ); - context.startActivity(webIntent); - }) - .setPositiveButton(R.string.accept, (dialog, which) -> { - final Intent i = new Intent(Intent.ACTION_SENDTO) - .setData(Uri.parse("mailto:")) // only email apps should handle this - .putExtra(Intent.EXTRA_EMAIL, new String[]{ERROR_EMAIL_ADDRESS}) - .putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT) - .putExtra(Intent.EXTRA_TEXT, buildJson()); - if (i.resolveActivity(getPackageManager()) != null) { - startActivity(i); - } - - }) - .setNegativeButton(R.string.decline, (dialog, which) -> { - // do nothing - }) - .show(); - + reportEmailButton.setOnClickListener((View v) -> { + openPrivacyPolicyDialog(this, "EMAIL"); }); + copyButton.setOnClickListener((View v) -> { + ShareUtils.copyToClipboard(this, buildMarkdown()); + Toast.makeText(this, R.string.msg_copied, Toast.LENGTH_SHORT).show(); + }); + + reportGithubButton.setOnClickListener((View v) -> { + openPrivacyPolicyDialog(this, "GITHUB"); + }); + + // normal bugreport buildInfo(errorInfo); if (errorInfo.message != 0) { @@ -250,7 +241,7 @@ public class ErrorActivity extends AppCompatActivity { errorView.setText(formErrorText(errorList)); - //print stack trace once again for debugging: + // print stack trace once again for debugging: for (String e : errorList) { Log.e(TAG, e); } @@ -281,6 +272,37 @@ public class ErrorActivity extends AppCompatActivity { return false; } + private void openPrivacyPolicyDialog(final Context context, final String action) { + new AlertDialog.Builder(context) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(R.string.privacy_policy_title) + .setMessage(R.string.start_accept_privacy_policy) + .setCancelable(false) + .setNeutralButton(R.string.read_privacy_policy, (dialog, which) -> { + ShareUtils.openUrlInBrowser(context, + context.getString(R.string.privacy_policy_url)); + }) + .setPositiveButton(R.string.accept, (dialog, which) -> { + if (action.equals("EMAIL")) { // send on email + final Intent i = new Intent(Intent.ACTION_SENDTO) + .setData(Uri.parse("mailto:")) // only email apps should handle this + .putExtra(Intent.EXTRA_EMAIL, new String[]{ERROR_EMAIL_ADDRESS}) + .putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT) + .putExtra(Intent.EXTRA_TEXT, buildJson()); + if (i.resolveActivity(getPackageManager()) != null) { + startActivity(i); + } + } else if (action.equals("GITHUB")) { // open the NewPipe issue page on GitHub + ShareUtils.openUrlInBrowser(this, ERROR_GITHUB_ISSUE_URL); + } + + }) + .setNegativeButton(R.string.decline, (dialog, which) -> { + // do nothing + }) + .show(); + } + private String formErrorText(final String[] el) { StringBuilder text = new StringBuilder(); if (el != null) { @@ -331,7 +353,9 @@ public class ErrorActivity extends AppCompatActivity { text += getUserActionString(info.userAction) + "\n" + info.request + "\n" - + getContentLangString() + "\n" + + getContentLanguageString() + "\n" + + getContentCountryString() + "\n" + + getAppLanguage() + "\n" + info.serviceName + "\n" + currentTimeStamp + "\n" + getPackageName() + "\n" @@ -347,7 +371,9 @@ public class ErrorActivity extends AppCompatActivity { .object() .value("user_action", getUserActionString(errorInfo.userAction)) .value("request", errorInfo.request) - .value("content_language", getContentLangString()) + .value("content_language", getContentLanguageString()) + .value("content_country", getContentCountryString()) + .value("app_language", getAppLanguage()) .value("service", errorInfo.serviceName) .value("package", getPackageName()) .value("version", BuildConfig.VERSION_NAME) @@ -365,6 +391,63 @@ public class ErrorActivity extends AppCompatActivity { return ""; } + private String buildMarkdown() { + try { + final StringBuilder htmlErrorReport = new StringBuilder(); + + final String userComment = userCommentBox.getText().toString(); + if (!userComment.isEmpty()) { + htmlErrorReport.append(userComment).append("\n"); + } + + // basic error info + htmlErrorReport + .append("## Exception") + .append("\n* __User Action:__ ") + .append(getUserActionString(errorInfo.userAction)) + .append("\n* __Request:__ ").append(errorInfo.request) + .append("\n* __Content Country:__ ").append(getContentCountryString()) + .append("\n* __Content Language:__ ").append(getContentLanguageString()) + .append("\n* __App Language:__ ").append(getAppLanguage()) + .append("\n* __Service:__ ").append(errorInfo.serviceName) + .append("\n* __Version:__ ").append(BuildConfig.VERSION_NAME) + .append("\n* __OS:__ ").append(getOsString()).append("\n"); + + + // Collapse all logs to a single paragraph when there are more than one + // to keep the GitHub issue clean. + if (errorList.length > 1) { + htmlErrorReport + .append("
Exceptions (") + .append(errorList.length) + .append(")

\n"); + } + + // add the logs + for (int i = 0; i < errorList.length; i++) { + htmlErrorReport.append("

Crash log "); + if (errorList.length > 1) { + htmlErrorReport.append(i + 1); + } + htmlErrorReport.append("") + .append("

\n") + .append("\n```\n").append(errorList[i]).append("\n```\n") + .append("

\n"); + } + + // make sure to close everything + if (errorList.length > 1) { + htmlErrorReport.append("

\n"); + } + htmlErrorReport.append("
\n"); + return htmlErrorReport.toString(); + } catch (Throwable e) { + Log.e(TAG, "Error while erroring: Could not build markdown"); + e.printStackTrace(); + return ""; + } + } + private String getUserActionString(final UserAction userAction) { if (userAction == null) { return "Your description is in another castle."; @@ -373,24 +456,27 @@ public class ErrorActivity extends AppCompatActivity { } } - private String getContentLangString() { - String contentLanguage = PreferenceManager.getDefaultSharedPreferences(this) - .getString(this.getString(R.string.content_country_key), "none"); - if (contentLanguage.equals(getString(R.string.default_localization_key))) { - contentLanguage = Locale.getDefault().toString(); - } - return contentLanguage; + private String getContentCountryString() { + return Localization.getPreferredContentCountry(this).getCountryCode(); + } + + private String getContentLanguageString() { + return Localization.getPreferredLocalization(this).getLocalizationCode(); + } + + private String getAppLanguage() { + return Localization.getAppLocale(getApplicationContext()).toString(); } private String getOsString() { - String osBase = Build.VERSION.SDK_INT >= 23 ? Build.VERSION.BASE_OS : "Android"; + final String osBase = Build.VERSION.SDK_INT >= 23 ? Build.VERSION.BASE_OS : "Android"; return System.getProperty("os.name") + " " + (osBase.isEmpty() ? "Android" : osBase) + " " + Build.VERSION.RELEASE + " - " + Build.VERSION.SDK_INT; } - private void addGuruMeditaion() { + private void addGuruMeditation() { //just an easter egg TextView sorryView = findViewById(R.id.errorSorryView); String text = sorryView.getText().toString(); diff --git a/app/src/main/res/layout/activity_error.xml b/app/src/main/res/layout/activity_error.xml index c47077c73..12237f918 100644 --- a/app/src/main/res/layout/activity_error.xml +++ b/app/src/main/res/layout/activity_error.xml @@ -118,11 +118,31 @@ android:inputType="" />