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}