Refactoring

pull/160/head
M66B 6 years ago
parent 83995877cf
commit 71cdeadcde

@ -19,7 +19,6 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B) Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/ */
import android.app.ActivityManager;
import android.app.Application; import android.app.Application;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel; import android.app.NotificationChannel;
@ -28,36 +27,11 @@ import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Build;
import android.os.DeadSystemException;
import android.os.RemoteException;
import android.view.OrientationEventListener;
import android.webkit.CookieManager; import android.webkit.CookieManager;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.bugsnag.android.BeforeNotify;
import com.bugsnag.android.BeforeSend;
import com.bugsnag.android.Bugsnag;
import com.bugsnag.android.Client;
import com.bugsnag.android.Error;
import com.bugsnag.android.Report;
import com.sun.mail.iap.ProtocolException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import javax.mail.MessagingException;
public class ApplicationEx extends Application { public class ApplicationEx extends Application {
private Thread.UncaughtExceptionHandler prev = null; private Thread.UncaughtExceptionHandler prev = null;
@ -71,7 +45,7 @@ public class ApplicationEx extends Application {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
logMemory("App create version=" + BuildConfig.VERSION_NAME); Log.logMemory(this, "App create version=" + BuildConfig.VERSION_NAME);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
final boolean crash_reports = prefs.getBoolean("crash_reports", false); final boolean crash_reports = prefs.getBoolean("crash_reports", false);
@ -81,12 +55,12 @@ public class ApplicationEx extends Application {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override @Override
public void uncaughtException(Thread thread, Throwable ex) { public void uncaughtException(Thread thread, Throwable ex) {
if (!crash_reports && ownFault(ex)) { if (!crash_reports && Log.ownFault(ex)) {
Log.e(ex); Log.e(ex);
if (BuildConfig.BETA_RELEASE || if (BuildConfig.BETA_RELEASE ||
!Helper.isPlayStoreInstall(ApplicationEx.this)) !Helper.isPlayStoreInstall(ApplicationEx.this))
writeCrashLog(ApplicationEx.this, ex); Log.writeCrash(ApplicationEx.this, ex);
if (prev != null) if (prev != null)
prev.uncaughtException(thread, ex); prev.uncaughtException(thread, ex);
@ -97,7 +71,7 @@ public class ApplicationEx extends Application {
} }
}); });
setupBugsnag(); Log.setupBugsnag(this);
upgrade(this); upgrade(this);
@ -115,156 +89,16 @@ public class ApplicationEx extends Application {
@Override @Override
public void onTrimMemory(int level) { public void onTrimMemory(int level) {
logMemory("Trim memory level=" + level); Log.logMemory(this, "Trim memory level=" + level);
super.onTrimMemory(level); super.onTrimMemory(level);
} }
@Override @Override
public void onLowMemory() { public void onLowMemory() {
logMemory("Low memory"); Log.logMemory(this, "Low memory");
super.onLowMemory(); super.onLowMemory();
} }
private void logMemory(String message) {
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
int mb = Math.round(mi.availMem / 0x100000L);
int perc = Math.round(mi.availMem / (float) mi.totalMem * 100.0f);
Log.i(message + " " + mb + " MB" + " " + perc + " %");
}
private void setupBugsnag() {
// https://docs.bugsnag.com/platforms/android/sdk/
com.bugsnag.android.Configuration config =
new com.bugsnag.android.Configuration("9d2d57476a0614974449a3ec33f2604a");
if (BuildConfig.DEBUG)
config.setReleaseStage("debug");
else {
String type = "other";
if (Helper.hasValidFingerprint(this))
if (BuildConfig.PLAY_STORE_RELEASE)
type = "play";
else
type = "full";
config.setReleaseStage(type + (BuildConfig.BETA_RELEASE ? "/beta" : ""));
}
config.setAutoCaptureSessions(false);
config.setDetectAnrs(false);
config.setDetectNdkCrashes(false);
List<String> ignore = new ArrayList<>();
ignore.add("com.sun.mail.util.MailConnectException");
ignore.add("android.accounts.OperationCanceledException");
ignore.add("android.app.RemoteServiceException");
ignore.add("java.lang.NoClassDefFoundError");
ignore.add("java.lang.UnsatisfiedLinkError");
ignore.add("java.nio.charset.MalformedInputException");
ignore.add("java.net.ConnectException");
ignore.add("java.net.SocketException");
ignore.add("java.net.SocketTimeoutException");
ignore.add("java.net.UnknownHostException");
ignore.add("javax.mail.AuthenticationFailedException");
ignore.add("javax.mail.FolderClosedException");
ignore.add("javax.mail.internet.AddressException");
ignore.add("javax.mail.MessageRemovedException");
ignore.add("javax.mail.ReadOnlyFolderException");
ignore.add("javax.mail.StoreClosedException");
ignore.add("org.xmlpull.v1.XmlPullParserException");
config.setIgnoreClasses(ignore.toArray(new String[0]));
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
config.beforeSend(new BeforeSend() {
@Override
public boolean run(@NonNull Report report) {
Error error = report.getError();
if (error != null) {
Throwable ex = error.getException();
if (ex instanceof MessagingException &&
(ex.getCause() instanceof IOException ||
ex.getCause() instanceof ProtocolException))
// IOException includes SocketException, SocketTimeoutException
// ProtocolException includes ConnectionException
return false;
if (ex instanceof MessagingException &&
("connection failure".equals(ex.getMessage()) ||
"failed to create new store connection".equals(ex.getMessage()) ||
"Failed to fetch headers".equals(ex.getMessage()) ||
"Failed to load IMAP envelope".equals(ex.getMessage()) ||
"Unable to load BODYSTRUCTURE".equals(ex.getMessage())))
return false;
if (ex instanceof IllegalStateException &&
("Not connected".equals(ex.getMessage()) ||
"This operation is not allowed on a closed folder".equals(ex.getMessage())))
return false;
if (ex instanceof FileNotFoundException &&
ex.getMessage() != null &&
(ex.getMessage().startsWith("Download image failed") ||
ex.getMessage().startsWith("https://ipinfo.io/") ||
ex.getMessage().startsWith("https://autoconfig.thunderbird.net/")))
return false;
}
return prefs.getBoolean("crash_reports", false); // opt-in
}
});
Bugsnag.init(this, config);
Client client = Bugsnag.getClient();
try {
Log.i("Disabling orientation listener");
Field fOrientationListener = Client.class.getDeclaredField("orientationListener");
fOrientationListener.setAccessible(true);
OrientationEventListener orientationListener = (OrientationEventListener) fOrientationListener.get(client);
orientationListener.disable();
Log.i("Disabled orientation listener");
} catch (Throwable ex) {
Log.e(ex);
}
String uuid = prefs.getString("uuid", null);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
prefs.edit().putString("uuid", uuid).apply();
}
Log.i("uuid=" + uuid);
client.setUserId(uuid);
if (prefs.getBoolean("crash_reports", false))
Bugsnag.startSession();
final String installer = getPackageManager().getInstallerPackageName(BuildConfig.APPLICATION_ID);
final boolean fingerprint = Helper.hasValidFingerprint(this);
Bugsnag.beforeNotify(new BeforeNotify() {
@Override
public boolean run(@NonNull Error error) {
error.addToTab("extra", "installer", installer == null ? "-" : installer);
error.addToTab("extra", "fingerprint", fingerprint);
error.addToTab("extra", "free", Log.getFreeMemMb());
return true;
}
});
}
static void upgrade(Context context) { static void upgrade(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
int version = prefs.getInt("version", BuildConfig.VERSION_CODE); int version = prefs.getInt("version", BuildConfig.VERSION_CODE);
@ -304,18 +138,6 @@ public class ApplicationEx extends Application {
editor.apply(); editor.apply();
} }
static Context getLocalizedContext(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean english = prefs.getBoolean("english", false);
if (english) {
Configuration config = new Configuration(context.getResources().getConfiguration());
config.setLocale(Locale.US);
return context.createConfigurationContext(config);
} else
return context;
}
private void createNotificationChannels() { private void createNotificationChannels() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
@ -378,61 +200,15 @@ public class ApplicationEx extends Application {
} }
} }
public boolean ownFault(Throwable ex) { static Context getLocalizedContext(Context context) {
if (ex instanceof OutOfMemoryError) SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return false; boolean english = prefs.getBoolean("english", false);
if (ex instanceof RemoteException)
return false;
/*
java.lang.NoSuchMethodError: No direct method ()V in class Landroid/security/IKeyChainService$Stub; or its super classes (declaration of 'android.security.IKeyChainService$Stub' appears in /system/framework/framework.jar!classes2.dex)
java.lang.NoSuchMethodError: No direct method ()V in class Landroid/security/IKeyChainService$Stub; or its super classes (declaration of 'android.security.IKeyChainService$Stub' appears in /system/framework/framework.jar!classes2.dex)
at com.android.keychain.KeyChainService$1.(KeyChainService.java:95)
at com.android.keychain.KeyChainService.(KeyChainService.java:95)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateService(AppComponentFactory.java:103)
*/
if (ex instanceof NoSuchMethodError)
return false;
if (ex.getMessage() != null &&
(ex.getMessage().startsWith("Bad notification posted") ||
ex.getMessage().contains("ActivityRecord not found") ||
ex.getMessage().startsWith("Unable to create layer")))
return false;
if (ex instanceof TimeoutException &&
ex.getMessage() != null &&
ex.getMessage().startsWith("com.sun.mail.imap.IMAPStore.finalize"))
return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
if (ex instanceof RuntimeException && ex.getCause() instanceof DeadSystemException)
return false;
if (BuildConfig.BETA_RELEASE)
return true;
while (ex != null) {
for (StackTraceElement ste : ex.getStackTrace())
if (ste.getClassName().startsWith(getPackageName()))
return true;
ex = ex.getCause();
}
return false;
}
static void writeCrashLog(Context context, Throwable ex) {
File file = new File(context.getCacheDir(), "crash.log");
Log.w("Writing exception to " + file);
try (FileWriter out = new FileWriter(file, true)) { if (english) {
out.write(BuildConfig.VERSION_NAME + " " + new Date() + "\r\n"); Configuration config = new Configuration(context.getResources().getConfiguration());
out.write(ex + "\r\n" + android.util.Log.getStackTraceString(ex) + "\r\n"); config.setLocale(Locale.US);
} catch (IOException e) { return context.createConfigurationContext(config);
Log.e(e); } else
} return context;
} }
} }

@ -105,7 +105,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.bugsnag.android.Bugsnag;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
@ -4510,7 +4509,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
prefs.edit().putBoolean("crash_reports", true).apply(); prefs.edit().putBoolean("crash_reports", true).apply();
if (cbNotAgain.isChecked()) if (cbNotAgain.isChecked())
prefs.edit().putBoolean("crash_reports_asked", true).apply(); prefs.edit().putBoolean("crash_reports_asked", true).apply();
Bugsnag.startSession(); Log.setCrashReporting(true);
} }
}) })
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {

@ -44,8 +44,6 @@ import androidx.appcompat.widget.SwitchCompat;
import androidx.constraintlayout.widget.Group; import androidx.constraintlayout.widget.Group;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.bugsnag.android.Bugsnag;
public class FragmentOptionsMisc extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener { public class FragmentOptionsMisc extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener {
private SwitchCompat swBadge; private SwitchCompat swBadge;
private SwitchCompat swSubscriptions; private SwitchCompat swSubscriptions;
@ -190,10 +188,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
.remove("crash_reports_asked") .remove("crash_reports_asked")
.putBoolean("crash_reports", checked) .putBoolean("crash_reports", checked)
.apply(); .apply();
if (checked) Log.setCrashReporting(checked);
Bugsnag.startSession();
else
Bugsnag.stopSession();
} }
}); });

@ -31,17 +31,27 @@ import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.DeadSystemException;
import android.os.Debug; import android.os.Debug;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.RemoteException;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.Display; import android.view.Display;
import android.view.OrientationEventListener;
import android.view.WindowManager; import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.bugsnag.android.BeforeNotify;
import com.bugsnag.android.BeforeSend;
import com.bugsnag.android.BreadcrumbType; import com.bugsnag.android.BreadcrumbType;
import com.bugsnag.android.Bugsnag; import com.bugsnag.android.Bugsnag;
import com.bugsnag.android.Client;
import com.bugsnag.android.Error;
import com.bugsnag.android.Report;
import com.bugsnag.android.Severity; import com.bugsnag.android.Severity;
import com.sun.mail.iap.ProtocolException;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -49,19 +59,26 @@ import org.json.JSONObject;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import javax.mail.Address; import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Part; import javax.mail.Part;
import javax.mail.internet.InternetAddress; import javax.mail.internet.InternetAddress;
@ -118,8 +135,143 @@ public class Log {
return android.util.Log.e(TAG, prefix + " " + ex + "\n" + android.util.Log.getStackTraceString(ex)); return android.util.Log.e(TAG, prefix + " " + ex + "\n" + android.util.Log.getStackTraceString(ex));
} }
static void setCrashReporting(boolean enabled) {
if (enabled)
Bugsnag.startSession();
else
Bugsnag.stopSession();
}
static void breadcrumb(String name, Map<String, String> crumb) { static void breadcrumb(String name, Map<String, String> crumb) {
Bugsnag.leaveBreadcrumb("operation", BreadcrumbType.LOG, crumb); Bugsnag.leaveBreadcrumb(name, BreadcrumbType.LOG, crumb);
}
static void setupBugsnag(Context context) {
// https://docs.bugsnag.com/platforms/android/sdk/
com.bugsnag.android.Configuration config =
new com.bugsnag.android.Configuration("9d2d57476a0614974449a3ec33f2604a");
if (BuildConfig.DEBUG)
config.setReleaseStage("debug");
else {
String type = "other";
if (Helper.hasValidFingerprint(context))
if (BuildConfig.PLAY_STORE_RELEASE)
type = "play";
else
type = "full";
config.setReleaseStage(type + (BuildConfig.BETA_RELEASE ? "/beta" : ""));
}
config.setAutoCaptureSessions(false);
config.setDetectAnrs(false);
config.setDetectNdkCrashes(false);
List<String> ignore = new ArrayList<>();
ignore.add("com.sun.mail.util.MailConnectException");
ignore.add("android.accounts.OperationCanceledException");
ignore.add("android.app.RemoteServiceException");
ignore.add("java.lang.NoClassDefFoundError");
ignore.add("java.lang.UnsatisfiedLinkError");
ignore.add("java.nio.charset.MalformedInputException");
ignore.add("java.net.ConnectException");
ignore.add("java.net.SocketException");
ignore.add("java.net.SocketTimeoutException");
ignore.add("java.net.UnknownHostException");
ignore.add("javax.mail.AuthenticationFailedException");
ignore.add("javax.mail.FolderClosedException");
ignore.add("javax.mail.internet.AddressException");
ignore.add("javax.mail.MessageRemovedException");
ignore.add("javax.mail.ReadOnlyFolderException");
ignore.add("javax.mail.StoreClosedException");
ignore.add("org.xmlpull.v1.XmlPullParserException");
config.setIgnoreClasses(ignore.toArray(new String[0]));
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
config.beforeSend(new BeforeSend() {
@Override
public boolean run(@NonNull Report report) {
Throwable ex = report.getError().getException();
if (ex instanceof MessagingException &&
(ex.getCause() instanceof IOException ||
ex.getCause() instanceof ProtocolException))
// IOException includes SocketException, SocketTimeoutException
// ProtocolException includes ConnectionException
return false;
if (ex instanceof MessagingException &&
("connection failure".equals(ex.getMessage()) ||
"failed to create new store connection".equals(ex.getMessage()) ||
"Failed to fetch headers".equals(ex.getMessage()) ||
"Failed to load IMAP envelope".equals(ex.getMessage()) ||
"Unable to load BODYSTRUCTURE".equals(ex.getMessage())))
return false;
if (ex instanceof IllegalStateException &&
("Not connected".equals(ex.getMessage()) ||
"This operation is not allowed on a closed folder".equals(ex.getMessage())))
return false;
if (ex instanceof FileNotFoundException &&
ex.getMessage() != null &&
(ex.getMessage().startsWith("Download image failed") ||
ex.getMessage().startsWith("https://ipinfo.io/") ||
ex.getMessage().startsWith("https://autoconfig.thunderbird.net/")))
return false;
return prefs.getBoolean("crash_reports", false); // opt-in
}
});
Bugsnag.init(context, config);
Client client = Bugsnag.getClient();
try {
Log.i("Disabling orientation listener");
Field fOrientationListener = Client.class.getDeclaredField("orientationListener");
fOrientationListener.setAccessible(true);
OrientationEventListener orientationListener = (OrientationEventListener) fOrientationListener.get(client);
orientationListener.disable();
Log.i("Disabled orientation listener");
} catch (Throwable ex) {
Log.e(ex);
}
String uuid = prefs.getString("uuid", null);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
prefs.edit().putString("uuid", uuid).apply();
}
Log.i("uuid=" + uuid);
client.setUserId(uuid);
if (prefs.getBoolean("crash_reports", false))
Bugsnag.startSession();
final String installer = context.getPackageManager().getInstallerPackageName(BuildConfig.APPLICATION_ID);
final boolean fingerprint = Helper.hasValidFingerprint(context);
Bugsnag.beforeNotify(new BeforeNotify() {
@Override
public boolean run(@NonNull Error error) {
error.addToTab("extra", "installer", installer == null ? "-" : installer);
error.addToTab("extra", "fingerprint", fingerprint);
error.addToTab("extra", "free", Log.getFreeMemMb());
return true;
}
});
} }
static void logExtras(Intent intent) { static void logExtras(Intent intent) {
@ -161,6 +313,73 @@ public class Log {
} }
} }
static void logMemory(Context context, String message) {
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
int mb = Math.round(mi.availMem / 0x100000L);
int perc = Math.round(mi.availMem / (float) mi.totalMem * 100.0f);
Log.i(message + " " + mb + " MB" + " " + perc + " %");
}
static boolean ownFault(Throwable ex) {
if (ex instanceof OutOfMemoryError)
return false;
if (ex instanceof RemoteException)
return false;
/*
java.lang.NoSuchMethodError: No direct method ()V in class Landroid/security/IKeyChainService$Stub; or its super classes (declaration of 'android.security.IKeyChainService$Stub' appears in /system/framework/framework.jar!classes2.dex)
java.lang.NoSuchMethodError: No direct method ()V in class Landroid/security/IKeyChainService$Stub; or its super classes (declaration of 'android.security.IKeyChainService$Stub' appears in /system/framework/framework.jar!classes2.dex)
at com.android.keychain.KeyChainService$1.(KeyChainService.java:95)
at com.android.keychain.KeyChainService.(KeyChainService.java:95)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateService(AppComponentFactory.java:103)
*/
if (ex instanceof NoSuchMethodError)
return false;
if (ex.getMessage() != null &&
(ex.getMessage().startsWith("Bad notification posted") ||
ex.getMessage().contains("ActivityRecord not found") ||
ex.getMessage().startsWith("Unable to create layer")))
return false;
if (ex instanceof TimeoutException &&
ex.getMessage() != null &&
ex.getMessage().startsWith("com.sun.mail.imap.IMAPStore.finalize"))
return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
if (ex instanceof RuntimeException && ex.getCause() instanceof DeadSystemException)
return false;
if (BuildConfig.BETA_RELEASE)
return true;
while (ex != null) {
for (StackTraceElement ste : ex.getStackTrace())
if (ste.getClassName().startsWith(BuildConfig.APPLICATION_ID))
return true;
ex = ex.getCause();
}
return false;
}
static void writeCrash(Context context, Throwable ex) {
File file = new File(context.getCacheDir(), "crash.log");
Log.w("Writing exception to " + file);
try (FileWriter out = new FileWriter(file, true)) {
out.write(BuildConfig.VERSION_NAME + " " + new Date() + "\r\n");
out.write(ex + "\r\n" + android.util.Log.getStackTraceString(ex) + "\r\n");
} catch (IOException e) {
Log.e(e);
}
}
static EntityMessage getDebugInfo(Context context, int title, Throwable ex, String log) throws IOException { static EntityMessage getDebugInfo(Context context, int title, Throwable ex, String log) throws IOException {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(context.getString(title)).append("\n\n\n\n"); sb.append(context.getString(title)).append("\n\n\n\n");

Loading…
Cancel
Save