mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-27 10:06:21 +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" />
|
||||
|
||||
<application android:label="@string/english_ime_name"
|
||||
android:name="org.dslul.openboard.inputmethod.latin.App"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* A simple class to help with removing directories recursively.
|
||||
|
@ -68,6 +69,10 @@ public class FileUtils {
|
|||
throw new IOException("could not create parent folder");
|
||||
}
|
||||
FileOutputStream out = new FileOutputStream(outfile);
|
||||
copyStreamToOtherStream(in, out);
|
||||
}
|
||||
|
||||
public static void copyStreamToOtherStream(InputStream in, OutputStream out) throws IOException {
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
|
|
|
@ -18,7 +18,9 @@ package org.dslul.openboard.inputmethod.latin.settings;
|
|||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
|
@ -28,12 +30,23 @@ import android.view.Menu;
|
|||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.BuildConfig;
|
||||
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.FeedbackUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.JniUtils;
|
||||
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 {
|
||||
// We don't care about menu grouping.
|
||||
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;
|
||||
// The second menu item id and order.
|
||||
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
|
||||
public void onCreate(final Bundle icicle) {
|
||||
|
@ -67,6 +83,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment {
|
|||
if (actionBar != null && screenTitle != null) {
|
||||
actionBar.setTitle(screenTitle);
|
||||
}
|
||||
if (BuildConfig.DEBUG)
|
||||
askAboutCrashReports();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,4 +128,64 @@ public final class SettingsFragment extends InputMethodSettingsFragment {
|
|||
}
|
||||
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