ExceptionHelper.java

  1package eu.siacs.conversations.utils;
  2
  3import android.content.Context;
  4import android.util.Log;
  5
  6import com.google.android.material.dialog.MaterialAlertDialogBuilder;
  7import com.google.common.base.Charsets;
  8import com.google.common.io.CharSink;
  9import com.google.common.io.Files;
 10
 11import eu.siacs.conversations.AppSettings;
 12import eu.siacs.conversations.Config;
 13import eu.siacs.conversations.R;
 14import eu.siacs.conversations.entities.Account;
 15import eu.siacs.conversations.entities.Conversation;
 16import eu.siacs.conversations.entities.Message;
 17import eu.siacs.conversations.services.XmppConnectionService;
 18import eu.siacs.conversations.ui.XmppActivity;
 19
 20import java.io.File;
 21import java.io.IOException;
 22import java.io.OutputStream;
 23import java.lang.ClassNotFoundException;
 24import java.text.SimpleDateFormat;
 25import java.util.Locale;
 26
 27public class ExceptionHelper {
 28
 29    private static final String FILENAME = "stacktrace.txt";
 30
 31    public static void init(final Context context) {
 32        if (Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler) {
 33            return;
 34        }
 35        Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(context));
 36    }
 37
 38    public static boolean checkForCrash(final XmppActivity activity) {
 39        try {
 40            Class.forName("io.sentry.Sentry");
 41            return false;
 42        } catch (final ClassNotFoundException e) { }
 43
 44        final XmppConnectionService service =
 45                activity == null ? null : activity.xmppConnectionService;
 46        if (service == null) {
 47            return false;
 48        }
 49        final AppSettings appSettings = new AppSettings(activity);
 50        if (!appSettings.isSendCrashReports() || Config.BUG_REPORTS == null) {
 51            return false;
 52        }
 53        final Account account = AccountUtils.getFirstEnabled(service);
 54        if (account == null) {
 55            return false;
 56        }
 57        final var file = new File(activity.getCacheDir(), FILENAME);
 58        if (!file.exists()) {
 59            return false;
 60        }
 61        final String report;
 62        try {
 63            report = Files.asCharSource(file, Charsets.UTF_8).read();
 64        } catch (final IOException e) {
 65            return false;
 66        }
 67        if (file.delete()) {
 68            Log.d(Config.LOGTAG, "deleted crash report file");
 69        }
 70        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
 71        builder.setTitle(
 72                activity.getString(
 73                        R.string.crash_report_title, activity.getString(R.string.app_name)));
 74        builder.setMessage(
 75                activity.getString(
 76                        R.string.crash_report_message, activity.getString(R.string.app_name)));
 77        builder.setPositiveButton(
 78                activity.getText(R.string.send_now),
 79                (dialog, which) -> {
 80                    Log.d(
 81                            Config.LOGTAG,
 82                            "using account="
 83                                    + account.getJid().asBareJid()
 84                                    + " to send in stack trace");
 85                    Conversation conversation =
 86                            service.findOrCreateConversation(
 87                                    account, Config.BUG_REPORTS, false, true);
 88                    Message message = new Message(conversation, report, Message.ENCRYPTION_NONE);
 89                    service.sendMessage(message);
 90                });
 91        builder.setNegativeButton(
 92                activity.getText(R.string.send_never),
 93                (dialog, which) -> appSettings.setSendCrashReports(false));
 94        builder.create().show();
 95        return true;
 96    }
 97
 98    static void writeToStacktraceFile(final Context context, final String msg) {
 99        try {
100            Files.asCharSink(new File(context.getCacheDir(), FILENAME), Charsets.UTF_8).write(msg);
101        } catch (IOException e) {
102            Log.w(Config.LOGTAG, "could not write stack trace to file", e);
103        }
104    }
105}