mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-28 18:48:09 +00:00
create crash report files in debug version
This commit is contained in:
parent
707a7bf48b
commit
356e39921b
4 changed files with 140 additions and 0 deletions
|
@ -41,6 +41,7 @@
|
||||||
android:protectionLevel="signature" />
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
<application android:label="@string/english_ime_name"
|
<application android:label="@string/english_ime_name"
|
||||||
|
android:name="org.dslul.openboard.inputmethod.latin.App"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package org.dslul.openboard.inputmethod.latin
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class App : Application() {
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
CrashReportExceptionHandler(applicationContext).install()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// basically copied from StreetComplete
|
||||||
|
private class CrashReportExceptionHandler(val appContext: Context) : Thread.UncaughtExceptionHandler {
|
||||||
|
private var defaultUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null
|
||||||
|
|
||||||
|
fun install(): Boolean {
|
||||||
|
val ueh = Thread.getDefaultUncaughtExceptionHandler()
|
||||||
|
check(ueh !is CrashReportExceptionHandler) { "May not install several CrashReportExceptionHandlers!" }
|
||||||
|
defaultUncaughtExceptionHandler = ueh
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(this)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||||
|
val stackTrace = StringWriter()
|
||||||
|
|
||||||
|
e.printStackTrace(PrintWriter(stackTrace))
|
||||||
|
writeCrashReportToFile("""
|
||||||
|
Thread: ${t.name}
|
||||||
|
App version: ${BuildConfig.VERSION_NAME}
|
||||||
|
Device: ${Build.BRAND} ${Build.DEVICE}, Android ${Build.VERSION.RELEASE}
|
||||||
|
Locale: ${Locale.getDefault()}
|
||||||
|
Stack trace:
|
||||||
|
$stackTrace
|
||||||
|
""")
|
||||||
|
defaultUncaughtExceptionHandler!!.uncaughtException(t, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeCrashReportToFile(text: String) {
|
||||||
|
try {
|
||||||
|
val dir = appContext.getExternalFilesDir(null) ?: return
|
||||||
|
val crashReportFile = File(dir, "crash_report_${System.currentTimeMillis()}.txt")
|
||||||
|
crashReportFile.writeText(text)
|
||||||
|
} catch (ignored: IOException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import java.io.FileOutputStream;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple class to help with removing directories recursively.
|
* A simple class to help with removing directories recursively.
|
||||||
|
@ -68,6 +69,10 @@ public class FileUtils {
|
||||||
throw new IOException("could not create parent folder");
|
throw new IOException("could not create parent folder");
|
||||||
}
|
}
|
||||||
FileOutputStream out = new FileOutputStream(outfile);
|
FileOutputStream out = new FileOutputStream(outfile);
|
||||||
|
copyStreamToOtherStream(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyStreamToOtherStream(InputStream in, OutputStream out) throws IOException {
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
int len;
|
int len;
|
||||||
while ((len = in.read(buf)) > 0) {
|
while ((len = in.read(buf)) > 0) {
|
||||||
|
|
|
@ -18,7 +18,9 @@ package org.dslul.openboard.inputmethod.latin.settings;
|
||||||
|
|
||||||
import android.app.ActionBar;
|
import android.app.ActionBar;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
|
@ -28,12 +30,23 @@ import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import org.dslul.openboard.inputmethod.latin.BuildConfig;
|
||||||
import org.dslul.openboard.inputmethod.latin.R;
|
import org.dslul.openboard.inputmethod.latin.R;
|
||||||
|
import org.dslul.openboard.inputmethod.latin.common.FileUtils;
|
||||||
import org.dslul.openboard.inputmethod.latin.utils.ApplicationUtils;
|
import org.dslul.openboard.inputmethod.latin.utils.ApplicationUtils;
|
||||||
import org.dslul.openboard.inputmethod.latin.utils.FeedbackUtils;
|
import org.dslul.openboard.inputmethod.latin.utils.FeedbackUtils;
|
||||||
import org.dslul.openboard.inputmethod.latin.utils.JniUtils;
|
import org.dslul.openboard.inputmethod.latin.utils.JniUtils;
|
||||||
import org.dslul.openboard.inputmethodcommon.InputMethodSettingsFragment;
|
import org.dslul.openboard.inputmethodcommon.InputMethodSettingsFragment;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
public final class SettingsFragment extends InputMethodSettingsFragment {
|
public final class SettingsFragment extends InputMethodSettingsFragment {
|
||||||
// We don't care about menu grouping.
|
// We don't care about menu grouping.
|
||||||
private static final int NO_MENU_GROUP = Menu.NONE;
|
private static final int NO_MENU_GROUP = Menu.NONE;
|
||||||
|
@ -41,6 +54,9 @@ public final class SettingsFragment extends InputMethodSettingsFragment {
|
||||||
private static final int MENU_ABOUT = Menu.FIRST;
|
private static final int MENU_ABOUT = Menu.FIRST;
|
||||||
// The second menu item id and order.
|
// The second menu item id and order.
|
||||||
private static final int MENU_HELP_AND_FEEDBACK = Menu.FIRST + 1;
|
private static final int MENU_HELP_AND_FEEDBACK = Menu.FIRST + 1;
|
||||||
|
private static final int CRASH_REPORT_REQUEST_CODE = 985287532;
|
||||||
|
// for storing crash report files, so onActivityResult can actually use them
|
||||||
|
private final ArrayList<File> crashReportFiles = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(final Bundle icicle) {
|
public void onCreate(final Bundle icicle) {
|
||||||
|
@ -67,6 +83,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment {
|
||||||
if (actionBar != null && screenTitle != null) {
|
if (actionBar != null && screenTitle != null) {
|
||||||
actionBar.setTitle(screenTitle);
|
actionBar.setTitle(screenTitle);
|
||||||
}
|
}
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
askAboutCrashReports();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,4 +128,64 @@ public final class SettingsFragment extends InputMethodSettingsFragment {
|
||||||
}
|
}
|
||||||
return Secure.getInt(activity.getContentResolver(), "user_setup_complete", 0) != 0;
|
return Secure.getInt(activity.getContentResolver(), "user_setup_complete", 0) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void askAboutCrashReports() {
|
||||||
|
// find crash report files
|
||||||
|
final File dir = getActivity().getExternalFilesDir(null);
|
||||||
|
if (dir == null) return;
|
||||||
|
// final File[] files = dir.listFiles((file, s) -> file.getName().startsWith("crash_report"));
|
||||||
|
final File[] allFiles = dir.listFiles();
|
||||||
|
if (allFiles == null) return;
|
||||||
|
crashReportFiles.clear();
|
||||||
|
for (File file : allFiles) {
|
||||||
|
if (file.getName().startsWith("crash_report"))
|
||||||
|
crashReportFiles.add(file);
|
||||||
|
}
|
||||||
|
if (crashReportFiles.isEmpty()) return;
|
||||||
|
new AlertDialog.Builder(getActivity())
|
||||||
|
.setMessage("Crash report files found")
|
||||||
|
.setPositiveButton("get", (dialogInterface, i) -> {
|
||||||
|
final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
intent.putExtra(Intent.EXTRA_TITLE, "crash_reports.zip");
|
||||||
|
intent.setType("application/zip");
|
||||||
|
startActivityForResult(intent, CRASH_REPORT_REQUEST_CODE);
|
||||||
|
})
|
||||||
|
.setNeutralButton("delete", (dialogInterface, i) -> {
|
||||||
|
for (File file : crashReportFiles) {
|
||||||
|
file.delete(); // don't care whether it fails, though user will complain
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("ignore", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (resultCode != Activity.RESULT_OK || data == null) return;
|
||||||
|
if (requestCode != CRASH_REPORT_REQUEST_CODE) return;
|
||||||
|
if (crashReportFiles.isEmpty()) return;
|
||||||
|
final Uri uri = data.getData();
|
||||||
|
if (uri == null) return;
|
||||||
|
final OutputStream os;
|
||||||
|
try {
|
||||||
|
os = getActivity().getContentResolver().openOutputStream(uri);
|
||||||
|
final BufferedOutputStream bos = new BufferedOutputStream(os);
|
||||||
|
final ZipOutputStream z = new ZipOutputStream(bos);
|
||||||
|
for (File file : crashReportFiles) {
|
||||||
|
FileInputStream f = new FileInputStream(file);
|
||||||
|
z.putNextEntry(new ZipEntry(file.getName()));
|
||||||
|
FileUtils.copyStreamToOtherStream(f, z);
|
||||||
|
f.close();
|
||||||
|
z.closeEntry();
|
||||||
|
}
|
||||||
|
z.close();
|
||||||
|
bos.close();
|
||||||
|
os.close();
|
||||||
|
for (File file : crashReportFiles) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue