diff --git a/app/build.gradle b/app/build.gradle
index 6e8507523c..367543c4b8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
+apply plugin: 'com.bugsnag.android.gradle'
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
@@ -86,6 +87,11 @@ android {
packagingOptions {
pickFirst 'META-INF/LICENSE.txt'
}
+
+ bugsnag {
+ // https://docs.bugsnag.com/build-integrations/gradle/
+ apiKey "9d2d57476a0614974449a3ec33f2604a"
+ }
}
repositories {
@@ -118,6 +124,7 @@ dependencies {
def requery_version = "3.28.0"
def badge_version = "1.1.22"
def photoview_version = "2.3.0"
+ def bugsnag_version = "4.14.0"
// https://developer.android.com/jetpack/androidx/releases/
@@ -199,6 +206,9 @@ dependencies {
// https://github.com/chrisbanes/PhotoView
implementation "com.github.chrisbanes:PhotoView:$photoview_version"
+ // https://github.com/bugsnag/bugsnag-android
+ implementation "com.bugsnag:bugsnag-android:$bugsnag_version"
+
// git clone https://android.googlesource.com/platform/frameworks/opt/colorpicker
implementation project(path: ':colorpicker')
}
diff --git a/app/src/main/java/eu/faircode/email/ApplicationEx.java b/app/src/main/java/eu/faircode/email/ApplicationEx.java
index 898450a907..9064f44c42 100644
--- a/app/src/main/java/eu/faircode/email/ApplicationEx.java
+++ b/app/src/main/java/eu/faircode/email/ApplicationEx.java
@@ -37,9 +37,14 @@ import android.os.Handler;
import android.os.RemoteException;
import android.webkit.CookieManager;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.preference.PreferenceManager;
+import com.bugsnag.android.BeforeSend;
+import com.bugsnag.android.Bugsnag;
+import com.bugsnag.android.Report;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -68,6 +73,7 @@ public class ApplicationEx extends Application {
@Override
public void onCreate() {
super.onCreate();
+
logMemory("App create version=" + BuildConfig.VERSION_NAME);
prev = Thread.getDefaultUncaughtExceptionHandler();
@@ -91,6 +97,30 @@ public class ApplicationEx extends Application {
}
});
+ // https://docs.bugsnag.com/platforms/android/sdk/
+ com.bugsnag.android.Configuration config =
+ new com.bugsnag.android.Configuration("9d2d57476a0614974449a3ec33f2604a");
+
+ if (BuildConfig.DEBUG)
+ config.setReleaseStage("development");
+ else if (BuildConfig.BETA_RELEASE)
+ config.setReleaseStage(BuildConfig.PLAY_STORE_RELEASE ? "beta/play" : "beta");
+ else
+ config.setReleaseStage(BuildConfig.PLAY_STORE_RELEASE ? "stable/play" : "stable");
+
+ config.setIgnoreClasses(new String[]{"javax.mail.MessageRemovedException"});
+
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+
+ config.beforeSend(new BeforeSend() {
+ @Override
+ public boolean run(@NonNull Report report) {
+ return prefs.getBoolean("crash_reports", false); // opt-out
+ }
+ });
+
+ Bugsnag.init(this, config);
+
upgrade(this);
createNotificationChannels();
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java
index 2eed3de874..6ab720eb87 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java
@@ -50,6 +50,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
private SwitchCompat swParanoid;
private TextView tvParanoidHint;
private SwitchCompat swUpdates;
+ private SwitchCompat swCrashReports;
private SwitchCompat swDebug;
private TextView tvLastCleanup;
@@ -57,7 +58,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
private Group grpSearchLocal;
private final static String[] RESET_OPTIONS = new String[]{
- "badge", "subscriptions", "search_local", "english", "authentication", "paranoid", "updates", "debug"
+ "badge", "subscriptions", "search_local", "english", "authentication", "paranoid", "updates", "crash_reports", "debug"
};
private final static String[] RESET_QUESTIONS = new String[]{
@@ -82,6 +83,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
swParanoid = view.findViewById(R.id.swParanoid);
tvParanoidHint = view.findViewById(R.id.tvParanoidHint);
swUpdates = view.findViewById(R.id.swUpdates);
+ swCrashReports = view.findViewById(R.id.swCrashReports);
swDebug = view.findViewById(R.id.swDebug);
tvLastCleanup = view.findViewById(R.id.tvLastCleanup);
@@ -162,6 +164,13 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
}
});
+ swCrashReports.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+ prefs.edit().putBoolean("crash_reports", checked).apply();
+ }
+ });
+
swDebug.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@@ -229,6 +238,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
swParanoid.setChecked(prefs.getBoolean("paranoid", true));
swUpdates.setChecked(prefs.getBoolean("updates", true));
swUpdates.setVisibility(Helper.isPlayStoreInstall(getContext()) ? View.GONE : View.VISIBLE);
+ swCrashReports.setChecked(prefs.getBoolean("crash_reports", false));
swDebug.setChecked(prefs.getBoolean("debug", false));
grpSearchLocal.setVisibility(Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M ? View.GONE : View.VISIBLE);
diff --git a/app/src/main/java/eu/faircode/email/Log.java b/app/src/main/java/eu/faircode/email/Log.java
index 4b8b87200e..328d2c23b2 100644
--- a/app/src/main/java/eu/faircode/email/Log.java
+++ b/app/src/main/java/eu/faircode/email/Log.java
@@ -23,6 +23,9 @@ import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
+import com.bugsnag.android.Bugsnag;
+import com.bugsnag.android.Severity;
+
import java.lang.reflect.Array;
import java.util.Set;
@@ -45,18 +48,22 @@ public class Log {
}
public static int w(Throwable ex) {
+ Bugsnag.notify(ex, Severity.WARNING);
return android.util.Log.w(TAG, ex + "\n" + android.util.Log.getStackTraceString(ex));
}
public static int e(Throwable ex) {
+ Bugsnag.notify(ex, Severity.ERROR);
return android.util.Log.e(TAG, ex + "\n" + android.util.Log.getStackTraceString(ex));
}
public static int w(String prefix, Throwable ex) {
+ Bugsnag.notify(ex, Severity.WARNING);
return android.util.Log.w(TAG, prefix + " " + ex + "\n" + android.util.Log.getStackTraceString(ex));
}
public static int e(String prefix, Throwable ex) {
+ Bugsnag.notify(ex, Severity.ERROR);
return android.util.Log.e(TAG, prefix + " " + ex + "\n" + android.util.Log.getStackTraceString(ex));
}
diff --git a/app/src/main/res/layout/fragment_options_misc.xml b/app/src/main/res/layout/fragment_options_misc.xml
index bc1bbb1bf3..b2261c4ef1 100644
--- a/app/src/main/res/layout/fragment_options_misc.xml
+++ b/app/src/main/res/layout/fragment_options_misc.xml
@@ -159,6 +159,16 @@
app:layout_constraintTop_toBottomOf="@id/tvParanoidHint"
app:switchPadding="12dp" />
+
+
Show a warning when the receiving server could not authenticate the message
Extra privacy features
Check for updates
+ Send crash reports
Debug mode
Last cleanup: %1$s
diff --git a/build.gradle b/build.gradle
index 0f527252c1..af0e3f8771 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,13 @@
buildscript {
-
+
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
+ // https://github.com/bugsnag/bugsnag-android-gradle-plugin
+ classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.2.0'
}
}