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.text.SimpleDateFormat;
24import java.util.Locale;
25
26public class ExceptionHelper {
27
28    private static final String FILENAME = "stacktrace.txt";
29
30    public static void init(final Context context) {
31        if (Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler) {
32            return;
33        }
34        Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(context));
35    }
36
37    public static boolean checkForCrash(final XmppActivity activity) {
38        final XmppConnectionService service =
39                activity == null ? null : activity.xmppConnectionService;
40        if (service == null) {
41            return false;
42        }
43        final AppSettings appSettings = new AppSettings(activity);
44        if (!appSettings.isSendCrashReports() || Config.BUG_REPORTS == null) {
45            return false;
46        }
47        final Account account = AccountUtils.getFirstEnabled(service);
48        if (account == null) {
49            return false;
50        }
51        final var file = new File(activity.getCacheDir(), FILENAME);
52        if (!file.exists()) {
53            return false;
54        }
55        final String report;
56        try {
57            report = Files.asCharSource(file, Charsets.UTF_8).read();
58        } catch (final IOException e) {
59            return false;
60        }
61        if (file.delete()) {
62            Log.d(Config.LOGTAG, "deleted crash report file");
63        }
64        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
65        builder.setTitle(
66                activity.getString(
67                        R.string.crash_report_title, activity.getString(R.string.app_name)));
68        builder.setMessage(
69                activity.getString(
70                        R.string.crash_report_message, activity.getString(R.string.app_name)));
71        builder.setPositiveButton(
72                activity.getText(R.string.send_now),
73                (dialog, which) -> {
74                    Log.d(
75                            Config.LOGTAG,
76                            "using account="
77                                    + account.getJid().asBareJid()
78                                    + " to send in stack trace");
79                    Conversation conversation =
80                            service.findOrCreateConversation(
81                                    account, Config.BUG_REPORTS, false, true);
82                    Message message = new Message(conversation, report, Message.ENCRYPTION_NONE);
83                    service.sendMessage(message);
84                });
85        builder.setNegativeButton(
86                activity.getText(R.string.send_never),
87                (dialog, which) -> appSettings.setSendCrashReports(false));
88        builder.create().show();
89        return true;
90    }
91
92    static void writeToStacktraceFile(final Context context, final String msg) {
93        try {
94            Files.asCharSink(new File(context.getCacheDir(), FILENAME), Charsets.UTF_8).write(msg);
95        } catch (IOException e) {
96            Log.w(Config.LOGTAG, "could not write stack trace to file", e);
97        }
98    }
99}