package com.devs.acr; import android.app.Application; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Environment; import android.os.StatFs; import android.text.Html; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Random; /** * @author Deven * * Licensed under the Apache License 2.0 license see: * http://www.apache.org/licenses/LICENSE-2.0 */ public class AutoErrorReporter implements Thread.UncaughtExceptionHandler { private static final String TAG = AutoErrorReporter.class.getSimpleName(); private static final boolean DEBUGABLE = false; private static String DEFAULT_EMAIL_SUBJECT = "ACR: New Crash Report Generated"; private String[] recipients ; private boolean startAttempted = false; private String versionName; //private String buildNumber; private String packageName; private String filePath; private String phoneModel; private String androidVersion; private String board; private String brand; private String device; private String display; private String fingerPrint; private String host; private String id; private String manufacturer; private String model; private String product; private String tags; private long time; private String type; private String user; private HashMap<String, String> customParameters = new HashMap<String, String>(); private Thread.UncaughtExceptionHandler previousHandler; private static AutoErrorReporter sInstance; private Application application; private AutoErrorReporter(Application application){ this.application = application; } public static AutoErrorReporter get(Application application) { if (sInstance == null) sInstance = new AutoErrorReporter(application); return sInstance; } public void start() { if(startAttempted) { showLog("Already started"); return; } previousHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); startAttempted = true; } /** * (Required) Defines one or more email addresses to send bug reports to. This method MUST be * called before calling start This method CANNOT be called after calling * start. * * @param emailAddresses one or more email addresses * @return the current AutoErrorReporterinstance (to allow for method chaining) */ public AutoErrorReporter setEmailAddresses(final String... emailAddresses) { if (startAttempted) { throw new IllegalStateException( "EmailAddresses must be set before start"); } this.recipients = emailAddresses; return this; } /** * (Optional) Defines a custom subject line to use for all bug reports. By default, reports will * use the string defined in DEFAULT_EMAIL_SUBJECT This method CANNOT be called * after calling start. * @param emailSubject custom email subject line * @return the current AutoErrorReporter instance (to allow for method chaining) */ public AutoErrorReporter setEmailSubject(final String emailSubject) { if (startAttempted) { throw new IllegalStateException("EmailSubject must be set before start"); } DEFAULT_EMAIL_SUBJECT = emailSubject; return this; } public void addCustomData(String Key, String Value) { customParameters.put(Key, Value); } private String createCustomInfoString() { String customInfo = ""; for (Object currentKey : customParameters.keySet()) { String currentVal = customParameters.get(currentKey); customInfo += currentKey + " = " + currentVal + "\n"; } return customInfo; } private long getAvailableInternalMemorySize() { File path = Environment.getDataDirectory(); StatFs stat = new StatFs(path.getPath()); long blockSize = stat.getBlockSize(); long availableBlocks = stat.getAvailableBlocks(); return (availableBlocks * blockSize)/(1024*1024); } private long getTotalInternalMemorySize() { File path = Environment.getDataDirectory(); StatFs stat = new StatFs(path.getPath()); long blockSize = stat.getBlockSize(); long totalBlocks = stat.getBlockCount(); return (totalBlocks * blockSize)/(1024*1024); } private void recordInformations(Context context) { try { PackageManager pm = context.getPackageManager(); PackageInfo pi; // Version pi = pm.getPackageInfo(context.getPackageName(), 0); versionName = pi.versionName; //buildNumber = currentVersionNumber(context); // Package name packageName = pi.packageName; // Device model phoneModel = Build.MODEL; // Android version androidVersion = Build.VERSION.RELEASE; board = Build.BOARD; brand = Build.BRAND; device = Build.DEVICE; display = Build.DISPLAY; fingerPrint = Build.FINGERPRINT; host = Build.HOST; id = Build.ID; model = Build.MODEL; product = Build.PRODUCT; manufacturer = Build.MANUFACTURER; tags = Build.TAGS; time = Build.TIME; type = Build.TYPE; user = Build.USER; } catch (Exception e) { e.printStackTrace(); } } private String createInformationString() { recordInformations(application); StringBuilder infoStringBuffer = new StringBuilder(); infoStringBuffer.append("\nVERSION : ").append(versionName); infoStringBuffer.append("\nPACKAGE : ").append(packageName); infoStringBuffer.append("\nFILE-PATH : ").append(filePath); infoStringBuffer.append("\nPHONE-MODEL : ").append(phoneModel); infoStringBuffer.append("\nANDROID_VERS : ").append(androidVersion); infoStringBuffer.append("\nBOARD : ").append(board); infoStringBuffer.append("\nBRAND : ").append(brand); infoStringBuffer.append("\nDEVICE : ").append(device); infoStringBuffer.append("\nDISPLAY : ").append(display); infoStringBuffer.append("\nFINGER-PRINT : ").append(fingerPrint); infoStringBuffer.append("\nHOST : ").append(host); infoStringBuffer.append("\nID : ").append(id); infoStringBuffer.append("\nMODEL : ").append(model); infoStringBuffer.append("\nPRODUCT : ").append(product); infoStringBuffer.append("\nMANUFACTURER : ").append(manufacturer); infoStringBuffer.append("\nTAGS : ").append(tags); infoStringBuffer.append("\nTIME : ").append(time); infoStringBuffer.append("\nTYPE : ").append(type); infoStringBuffer.append("\nUSER : ").append(user); infoStringBuffer.append("\nTOTAL-INTERNAL-MEMORY : ").append(getTotalInternalMemorySize()+" mb"); infoStringBuffer.append("\nAVAILABLE-INTERNAL-MEMORY : ").append(getAvailableInternalMemorySize()+" mb"); return infoStringBuffer.toString(); } public void uncaughtException(Thread t, Throwable e) { showLog("====uncaughtException"); StringBuilder reportStringBuffer = new StringBuilder(); reportStringBuffer.append("Error Report collected on : ").append(new Date().toString()); reportStringBuffer.append("\n\nInformations :\n=============="); reportStringBuffer.append(createInformationString()); String customInfo = createCustomInfoString(); if(!customInfo.equals("")) { reportStringBuffer.append("\n\nCustom Informations :\n==============\n"); reportStringBuffer.append(customInfo); } reportStringBuffer.append("\n\nStack :\n==============\n"); final Writer result = new StringWriter(); final PrintWriter printWriter = new PrintWriter(result); e.printStackTrace(printWriter); reportStringBuffer.append(result.toString()); reportStringBuffer.append("\nCause :\n=============="); // If the exception was thrown in a background thread inside // AsyncTask, then the actual exception can be found with getCause Throwable cause = e.getCause(); while (cause != null) { cause.printStackTrace(printWriter); reportStringBuffer.append(result.toString()); cause = cause.getCause(); } printWriter.close(); reportStringBuffer.append("\n\n**** End of current Report ***"); showLog("====uncaughtException \n Report: "+reportStringBuffer.toString()); saveAsFile(reportStringBuffer.toString()); Intent intent = new Intent(application, ErrorReporterActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); application.startActivity(intent); //previousHandler.uncaughtException(t, e); android.os.Process.killProcess(android.os.Process.myPid()); System.exit(10); } private void sendErrorMail(Context _context, String errorContent) { showLog("====sendErrorMail"); Intent sendIntent = new Intent(Intent.ACTION_SEND); String subject = DEFAULT_EMAIL_SUBJECT; String body = "\n\n" + errorContent + "\n\n"; sendIntent.putExtra(Intent.EXTRA_EMAIL, recipients); sendIntent.putExtra(Intent.EXTRA_TEXT, body); sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject); sendIntent.setType("message/rfc822"); //sendIntent.setType("text/html"); sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); _context.startActivity(Intent.createChooser(sendIntent, "Title:")); } private void saveAsFile(String errorContent) { showLog("====SaveAsFile"); try { Random generator = new Random(); int random = generator.nextInt(99999); String FileName = "stack-" + random + ".stacktrace"; FileOutputStream trace = application.openFileOutput(FileName, Context.MODE_PRIVATE); trace.write(errorContent.getBytes()); trace.close(); } catch (Exception e) { // ... } } private String[] getErrorFileList() { File dir = new File(filePath + "/"); // Try to create the files folder if it doesn't exist dir.mkdir(); // Filter for ".stacktrace" files FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".stacktrace"); } }; return dir.list(filter); } private boolean bIsThereAnyErrorFile() { return getErrorFileList().length > 0; } void checkErrorAndSendMail(Context _context) { try { filePath = _context.getFilesDir().getAbsolutePath(); if (bIsThereAnyErrorFile()) { StringBuilder wholeErrorTextSB = new StringBuilder(); String[] errorFileList = getErrorFileList(); int curIndex = 0; final int maxSendMail = 5; for (String curString : errorFileList) { if (curIndex++ <= maxSendMail) { wholeErrorTextSB.append("New Trace collected :\n=====================\n"); String filePathStr = filePath + "/" + curString; BufferedReader input = new BufferedReader( new FileReader(filePathStr)); String line; while ((line = input.readLine()) != null) { wholeErrorTextSB.append(line + "\n"); } input.close(); } // DELETE FILES !!!! File curFile = new File(filePath + "/" + curString); curFile.delete(); } sendErrorMail(_context, wholeErrorTextSB.toString()); } } catch (Exception e) { e.printStackTrace(); } } private void showLog(String msg){ if(DEBUGABLE) Log.i(TAG, msg); } }