diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..a4763d1
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
index 6a165b7..a334ecf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@ _site
MANIFEST.MF
build
build.num
+bin
+gen
*.jar
diff --git a/.project b/.project
new file mode 100644
index 0000000..6e04ac4
--- /dev/null
+++ b/.project
@@ -0,0 +1,33 @@
+
+
+ errbit-android
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..b080d2d
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..7415177
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/proguard-project.txt b/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/project.properties b/project.properties
new file mode 100644
index 0000000..484dab0
--- /dev/null
+++ b/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
+android.library=true
diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml
new file mode 100644
index 0000000..541752f
--- /dev/null
+++ b/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml
new file mode 100644
index 0000000..f20e015
--- /dev/null
+++ b/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..e5f8fdc
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..4a10ca4
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/welaika/android/errbit/ErrbitNotifier.java b/src/com/welaika/android/errbit/ErrbitNotifier.java
index 7e5ca76..648be24 100644
--- a/src/com/welaika/android/errbit/ErrbitNotifier.java
+++ b/src/com/welaika/android/errbit/ErrbitNotifier.java
@@ -20,412 +20,532 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-*/
+ */
package com.welaika.android.errbit;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Random;
import org.xmlpull.v1.XmlSerializer;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.os.AsyncTask;
import android.util.Log;
import android.util.Xml;
/**
* ErrBit Notifier
- *
+ *
* Logs exceptions to Errbit
*
- * Based on https://github.com/loopj/airbrake-android/ v.1.3.0
+ * Based on https://github.com/loopj/airbrake-android/ v.1.3.0
*/
public class ErrbitNotifier {
- private static final String LOG_TAG = "ErrbitNotifier";
-
- private static final String AIRBRAKE_API_VERSION = "2.0";
-
- private static final String NOTIFIER_NAME = "Android Errbit Notifier";
- private static final String NOTIFIER_VERSION = "0.2.0";
- private static final String NOTIFIER_URL = "http://welaika.com";
-
- private static final String UNSENT_EXCEPTION_PATH = "/unsent_errbit_exceptions/";
-
- private static final String ENVIRONMENT_PRODUCTION = "production";
- private static final String ENVIRONMENT_DEFAULT = ENVIRONMENT_PRODUCTION;
-
- // Exception meta-data
- private static String environmentName = ENVIRONMENT_DEFAULT;
- private static String packageName = "unknown";
- private static String versionName = "unknown";
- private static String phoneModel = android.os.Build.MODEL;
- private static String androidVersion = android.os.Build.VERSION.RELEASE;
- private static String brandDevice = android.os.Build.BRAND;
- private static String manufacturerDevice = android.os.Build.MANUFACTURER;
-
-
- // Anything extra the app wants to add
- private static Map extraData;
-
- // Errbit api key
- private static String apiKey;
-
- // Errbit api key
- private static String errbit_endpoint = "http://airbrakeapp.com/notifier_api/v2/notices";
-
- // Exception storage info
- private static boolean notifyOnlyProduction = false;
- private static String filePath;
- private static boolean diskStorageEnabled = false;
-
- // Wrapper class to send uncaught exceptions to errbit
- private static class ErrbitExceptionHandler implements UncaughtExceptionHandler {
- private UncaughtExceptionHandler defaultExceptionHandler;
-
- public ErrbitExceptionHandler(UncaughtExceptionHandler defaultExceptionHandlerIn) {
- defaultExceptionHandler = defaultExceptionHandlerIn;
- }
-
- public void uncaughtException(Thread t, Throwable e) {
- ErrbitNotifier.notify(e);
- defaultExceptionHandler.uncaughtException(t, e);
- }
- }
-
- // Register to send exceptions to airbrake
- public static void register(Context context, String endpoint, String apiKey) {
- register(context, endpoint, apiKey, ENVIRONMENT_DEFAULT, true);
- }
-
- public static void register(Context context, String endpoint, String apiKey, String environmentName) {
- register(context, endpoint, apiKey, environmentName, true);
- }
-
- public static void register(Context context, String endpoint, String apiKey, String environmentName, boolean notifyOnlyProduction) {
- // Require an airbrake api key
- if(apiKey != null) {
- ErrbitNotifier.apiKey = apiKey;
- } else {
- throw new RuntimeException("ErrBitNotifier requires an API key.");
- }
-
- if(endpoint != null) {
- ErrbitNotifier.errbit_endpoint = "http://" + endpoint + "/notifier_api/v2/notices";
- } else {
- ErrbitNotifier.errbit_endpoint = "http://airbrakeapp.com/notifier_api/v2/notices";
- }
-
- // Checked if context is passed
- if(context == null) {
- throw new IllegalArgumentException("context cannot be null.");
- }
-
- // Fill in environment name if passed
- if(environmentName != null) {
- ErrbitNotifier.environmentName = environmentName;
- }
-
- // Check which exception types to notify
- ErrbitNotifier.notifyOnlyProduction = notifyOnlyProduction;
-
- // Connect our default exception handler
- UncaughtExceptionHandler currentHandler = Thread.getDefaultUncaughtExceptionHandler();
- if(!(currentHandler instanceof ErrbitExceptionHandler) && (environmentName.equals(ENVIRONMENT_PRODUCTION) || !notifyOnlyProduction)) {
- Thread.setDefaultUncaughtExceptionHandler(new ErrbitExceptionHandler(currentHandler));
- }
-
- // Load up current package name and version
- try {
- packageName = context.getPackageName();
- PackageInfo pi = context.getPackageManager().getPackageInfo(packageName, 0);
- if(pi.versionName != null) {
- versionName = pi.versionName;
- }
- } catch (Exception e) {}
-
- // Prepare the file storage location
- // TODO: Does this need to be done in a background thread?
- filePath = context.getFilesDir().getAbsolutePath() + UNSENT_EXCEPTION_PATH;
- File outFile = new File(filePath);
- outFile.mkdirs();
- diskStorageEnabled = outFile.exists();
-
- Log.d(LOG_TAG, "Registered and ready to handle exceptions.");
-
- // Flush any existing exception info
- new AsyncTask () {
- protected Void doInBackground(Void... voi) {
- flushExceptions();
- return null;
- }
- }.execute();
- }
-
- /**
- * Add a custom set of key/value data that will be sent as session data with each notification
- * @param extraData a Map of String -> String
- */
- public static void setExtraData(Map extraData) {
- ErrbitNotifier.extraData = extraData;
- }
-
- // Fire an exception to airbrake manually
- public static void notify(final Throwable e) {
- notify(e, null);
- }
-
- public static void notify(final Throwable e, final Map metaData) {
- if(e != null && diskStorageEnabled) {
- new AsyncTask () {
- protected Void doInBackground(Void... voi) {
- writeExceptionToDisk(e, metaData);
- flushExceptions();
- return null;
- }
- }.execute();
- }
- }
-
- private static void writeExceptionToDisk(Throwable e, final Map metaData) {
- try {
- // Set up the output stream
- int random = new Random().nextInt(99999);
- String filename = filePath + versionName + "-" + String.valueOf(random) + ".xml";
- BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
- XmlSerializer s = Xml.newSerializer();
- s.setOutput(writer);
-
- // Start ridiculous xml building
- s.startDocument("UTF-8", true);
-
- // Top level tag
- s.startTag("", "notice");
- s.attribute("", "version", AIRBRAKE_API_VERSION);
-
- // Fill in the api key
- s.startTag("", "api-key");
- s.text(apiKey);
- s.endTag("", "api-key");
-
- // Fill in the notifier data
- s.startTag("", "notifier");
- s.startTag("", "name");
- s.text(NOTIFIER_NAME);
- s.endTag("", "name");
- s.startTag("", "version");
- s.text(NOTIFIER_VERSION);
- s.endTag("", "version");
- s.startTag("", "url");
- s.text(NOTIFIER_URL);
- s.endTag("", "url");
- s.endTag("", "notifier");
-
- // Fill in the error info
- s.startTag("", "error");
- s.startTag("", "class");
- s.text(e.getClass().getName());
- s.endTag("", "class");
- s.startTag("", "message");
- s.text("[" + versionName + "] " + e.toString());
- s.endTag("", "message");
-
- // Extract the stack traces
- s.startTag("", "backtrace");
- Throwable currentEx = e;
- while(currentEx != null) {
- // Catch some inner exceptions without discarding the entire report
- try {
- StackTraceElement[] stackTrace = currentEx.getStackTrace();
- for(StackTraceElement el : stackTrace) {
- s.startTag("", "line");
- try{
- s.attribute("", "method", el.getClassName() + "." + el.getMethodName());
- s.attribute("", "file", el.getFileName() == null ? "Unknown" : el.getFileName());
- s.attribute("", "number", String.valueOf(el.getLineNumber()));
- }catch(Throwable ex){
- Log.v(LOG_TAG, "Exception caught:",ex);
- }
- s.endTag("", "line");
- }
-
- currentEx = currentEx.getCause();
- if(currentEx != null) {
- s.startTag("", "line");
- try{
- s.attribute("", "file", "### CAUSED BY ###: " + currentEx.toString());
- s.attribute("", "number", "");
- }catch(Throwable ex){
- Log.v(LOG_TAG, "Exception caught:",ex);
- }
- s.endTag("", "line");
- }
- } catch(Throwable innerException) {
- Log.v(LOG_TAG, "Exception caught:",e);
- break;
- }
- }
- s.endTag("", "backtrace");
- s.endTag("", "error");
-
- // Additional request info
- s.startTag("", "request");
-
- s.startTag("", "url");
- s.endTag("", "url");
- s.startTag("", "component");
- s.endTag("", "component");
- s.startTag("", "action");
- s.endTag("", "action");
- s.startTag("", "cgi-data");
-
- s.startTag("", "var");
- s.attribute("", "key", "Manufacturer");
- s.text(manufacturerDevice);
- s.endTag("", "var");
-
- s.startTag("", "var");
- s.attribute("", "key", "Device");
- s.text(phoneModel);
- s.endTag("", "var");
-
- s.startTag("", "var");
- s.attribute("", "key", "Brand");
- s.text(brandDevice);
- s.endTag("", "var");
-
- s.startTag("", "var");
- s.attribute("", "key", "Android Version");
- s.text(androidVersion);
- s.endTag("", "var");
-
- s.startTag("", "var");
- s.attribute("", "key", "App Version");
- s.text(versionName);
- s.endTag("", "var");
-
- // Extra info, if present
- if (extraData != null && !extraData.isEmpty()) {
- for (Map.Entry extra : extraData.entrySet()) {
- s.startTag("", "var");
- s.attribute("", "key", extra.getKey());
- s.text(extra.getValue());
- s.endTag("", "var");
- }
- }
-
- // Metadata, if present
- if (metaData != null && !metaData.isEmpty()) {
- for (Map.Entry extra : metaData.entrySet()) {
- s.startTag("", "var");
- s.attribute("", "key", extra.getKey());
- s.text(extra.getValue());
- s.endTag("", "var");
- }
- }
-
- s.endTag("", "cgi-data");
- s.endTag("", "request");
-
- // Production/development mode flag and app version
- s.startTag("", "server-environment");
- s.startTag("", "environment-name");
- s.text(environmentName);
- s.endTag("", "environment-name");
- s.startTag("", "app-version");
- s.text(versionName);
- s.endTag("", "app-version");
- s.endTag("", "server-environment");
-
- // Close document
- s.endTag("", "notice");
- s.endDocument();
-
- // Flush to disk
- writer.flush();
- writer.close();
-
- Log.d(LOG_TAG, "Writing new " + e.getClass().getName() + " exception to disk.");
- } catch (Exception ex) {
- Log.v(LOG_TAG, "Exception caught:",ex);
- }
- }
-
- private static void sendExceptionData(File file) {
- try {
- boolean sent = false;
- URL url = new URL(errbit_endpoint);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- try {
- // Set up the connection
- conn.setDoOutput(true);
- conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
-
- // Read from the file and send it
- FileInputStream is = new FileInputStream(file);
- OutputStream os = conn.getOutputStream();
- byte[] buffer = new byte[4096];
- int numRead;
- while((numRead = is.read(buffer)) >= 0) {
- os.write(buffer, 0, numRead);
- }
- os.flush();
- os.close();
- is.close();
-
- // Flush the request through
- int response = conn.getResponseCode();
- Log.d(LOG_TAG, "Sent exception file " + file.getName() + " to Errbit. Got response code " + String.valueOf(response));
-
- sent = true;
-
- } catch(IOException e) {
-
- Log.v(LOG_TAG, "Exception caught:",e);
-
- } finally {
-
- // delete the file only if sending was successful
- if ( sent ) {
- file.delete();
- }
-
- conn.disconnect();
- }
-
- } catch(Exception e) {
-
- // unknown exception
- Log.v(LOG_TAG, "Exception caught:",e);
-
- }
- }
-
- private static synchronized void flushExceptions() {
- File exceptionDir = new File(filePath);
- if(exceptionDir.exists() && exceptionDir.isDirectory()) {
- File[] exceptions = exceptionDir.listFiles();
- for(File f : exceptions) {
- if(f.exists() && f.isFile()) {
- sendExceptionData(f);
- }
- }
- }
- }
+ private static final String LOG_TAG = "ErrbitNotifier";
+
+ private static final String AIRBRAKE_API_VERSION = "2.0";
+
+ private static final String NOTIFIER_NAME = "Android Errbit Notifier";
+ private static final String NOTIFIER_VERSION = "0.2.0";
+ private static final String NOTIFIER_URL = "http://welaika.com";
+
+ private static final String UNSENT_EXCEPTION_PATH = "/unsent_errbit_exceptions/";
+
+ private static final String ENVIRONMENT_PRODUCTION = "production";
+ private static final String ENVIRONMENT_DEFAULT = ENVIRONMENT_PRODUCTION;
+
+ // Exception meta-data
+ private static String environmentName = ENVIRONMENT_DEFAULT;
+ private static String packageName = "unknown";
+ private static String versionName = "unknown";
+ private static String phoneModel = android.os.Build.MODEL;
+ private static String androidVersion = android.os.Build.VERSION.RELEASE;
+ private static String brandDevice = android.os.Build.BRAND;
+ private static String manufacturerDevice = android.os.Build.MANUFACTURER;
+
+ // Anything extra the app wants to add
+ private static Map environmentData;
+ private static Map sessionData = new HashMap();
+ private static Map requestData = new HashMap();
+
+ private static Resources defaultResources;
+
+ // Errbit api key
+ private static String apiKey;
+
+ // Errbit api key
+ private static String errbit_endpoint = "http://airbrakeapp.com/notifier_api/v2/notices";
+
+ // Exception storage info
+ private static String filePath;
+ private static boolean diskStorageEnabled = false;
+
+ // Wrapper class to send uncaught exceptions to errbit
+ private static class ErrbitExceptionHandler implements
+ UncaughtExceptionHandler {
+ private UncaughtExceptionHandler defaultExceptionHandler;
+
+ public ErrbitExceptionHandler(
+ UncaughtExceptionHandler defaultExceptionHandlerIn) {
+ defaultExceptionHandler = defaultExceptionHandlerIn;
+ }
+
+ public void uncaughtException(Thread t, Throwable e) {
+ ErrbitNotifier.notify(e);
+ defaultExceptionHandler.uncaughtException(t, e);
+ }
+ }
+
+ // Register to send exceptions to airbrake
+ public static void register(Context context, String endpoint, String apiKey) {
+ register(context, endpoint, apiKey, ENVIRONMENT_DEFAULT, true);
+ }
+
+ public static void register(Context context, String endpoint,
+ String apiKey, String environmentName) {
+ register(context, endpoint, apiKey, environmentName, true);
+ }
+
+ public static void register(Context context, String endpoint,
+ String apiKey, String environmentName, boolean notifyOnlyProduction) {
+ // Require an airbrake api key
+ if (apiKey != null) {
+ ErrbitNotifier.apiKey = apiKey;
+ } else {
+ throw new RuntimeException("ErrBitNotifier requires an API key.");
+ }
+
+ if (endpoint != null) {
+ ErrbitNotifier.errbit_endpoint = "http://" + endpoint
+ + "/notifier_api/v2/notices";
+ } else {
+ ErrbitNotifier.errbit_endpoint = "http://airbrakeapp.com/notifier_api/v2/notices";
+ }
+
+ // Checked if context is passed
+ if (context == null) {
+ throw new IllegalArgumentException("context cannot be null.");
+ }
+
+ // Fill in environment name if passed
+ if (environmentName != null) {
+ ErrbitNotifier.environmentName = environmentName;
+ }
+
+ // Connect our default exception handler
+ UncaughtExceptionHandler currentHandler = Thread
+ .getDefaultUncaughtExceptionHandler();
+ if (!(currentHandler instanceof ErrbitExceptionHandler)
+ && (environmentName.equals(ENVIRONMENT_PRODUCTION) || !notifyOnlyProduction)) {
+ Thread.setDefaultUncaughtExceptionHandler(new ErrbitExceptionHandler(
+ currentHandler));
+ }
+
+ // Load up current package name and version
+ try {
+ packageName = context.getPackageName();
+ PackageInfo pi = context.getPackageManager().getPackageInfo(
+ packageName, 0);
+ if (pi.versionName != null) {
+ versionName = pi.versionName;
+ }
+ } catch (Exception e) {
+ }
+
+ // Prepare the file storage location
+ // TODO: Does this need to be done in a background thread?
+ filePath = context.getFilesDir().getAbsolutePath()
+ + UNSENT_EXCEPTION_PATH;
+ File outFile = new File(filePath);
+ outFile.mkdirs();
+ diskStorageEnabled = outFile.exists();
+
+ Log.d(LOG_TAG, "Registered and ready to handle exceptions.");
+
+ // Flush any existing exception info
+ new AsyncTask() {
+ protected Void doInBackground(Void... voi) {
+ flushExceptions();
+ return null;
+ }
+ }.execute();
+ }
+
+ /**
+ * Add a custom set of key/value data that will be sent as session data with
+ * each notification
+ *
+ * @param extraData
+ * a Map of String -> String
+ */
+ public static void setEnvironmentData(Map extraData) {
+ ErrbitNotifier.environmentData = extraData;
+ }
+
+ /**
+ * set specific environment value
+ *
+ * @param key
+ * @param value
+ */
+ public static void storeEnvironmentValue(String key, String value) {
+ if (environmentData == null) {
+ environmentData = new HashMap();
+ }
+ environmentData.put(key, value);
+ }
+
+ /**
+ * Store session value
+ *
+ * @param key
+ * @param value
+ */
+ public static void storeSessionValue(String key, String value) {
+ sessionData.put(key, value);
+ }
+
+ public static void setDefaultResources(Resources res) {
+ defaultResources = res;
+ }
+
+ /**
+ * Store request value
+ *
+ * @param key
+ * @param value
+ */
+ public static void storeRequestValue(String key, String value) {
+ requestData.put(key, value);
+ }
+
+ // Fire an exception to airbrake manually
+ public static void notify(final Throwable e) {
+ notify(e, null, null);
+ }
+
+ public static void notify(final Throwable e, Resources res) {
+ notify(e, null, res);
+ }
+
+ public static void notify(final Throwable e,
+ final Map metaData) {
+ notify(e, metaData, null);
+ }
+
+ public static void notify(final Throwable e,
+ final Map metaData, final Resources res) {
+ if (e != null && diskStorageEnabled) {
+ new AsyncTask() {
+ protected Void doInBackground(Void... voi) {
+ writeExceptionToDisk(e, metaData, res);
+ flushExceptions();
+ return null;
+ }
+ }.execute();
+ }
+ }
+
+ private static void writeExceptionToDisk(Throwable e,
+ final Map metaData, Resources res) {
+ try {
+ // Set up the output stream
+ int random = new Random().nextInt(99999);
+ String filename = filePath + versionName + "-"
+ + String.valueOf(random) + ".xml";
+ BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
+ XmlSerializer s = Xml.newSerializer();
+ s.setOutput(writer);
+
+ // Start ridiculous xml building
+ s.startDocument("UTF-8", true);
+
+ // Top level tag
+ s.startTag("", "notice");
+ s.attribute("", "version", AIRBRAKE_API_VERSION);
+
+ // Fill in the api key
+ s.startTag("", "api-key");
+ s.text(apiKey);
+ s.endTag("", "api-key");
+
+ // Fill in the notifier data
+ s.startTag("", "notifier");
+ s.startTag("", "name");
+ s.text(NOTIFIER_NAME);
+ s.endTag("", "name");
+ s.startTag("", "version");
+ s.text(NOTIFIER_VERSION);
+ s.endTag("", "version");
+ s.startTag("", "url");
+ s.text(NOTIFIER_URL);
+ s.endTag("", "url");
+ s.endTag("", "notifier");
+
+ // Fill in the error info
+ s.startTag("", "error");
+ s.startTag("", "class");
+ s.text(e.getClass().getName());
+ s.endTag("", "class");
+ s.startTag("", "message");
+ s.text("[" + versionName + "] " + e.toString());
+ s.endTag("", "message");
+
+ // Extract the stack traces
+ s.startTag("", "backtrace");
+ Throwable currentEx = e;
+ while (currentEx != null) {
+ // Catch some inner exceptions without discarding the entire
+ // report
+ try {
+ StackTraceElement[] stackTrace = currentEx.getStackTrace();
+ for (StackTraceElement el : stackTrace) {
+ s.startTag("", "line");
+ try {
+ s.attribute("", "method", el.getClassName() + "."
+ + el.getMethodName());
+ s.attribute(
+ "",
+ "file",
+ el.getFileName() == null ? "Unknown" : el
+ .getFileName());
+ s.attribute("", "number",
+ String.valueOf(el.getLineNumber()));
+ } catch (Throwable ex) {
+ Log.v(LOG_TAG, "Exception caught:", ex);
+ }
+ s.endTag("", "line");
+ }
+
+ currentEx = currentEx.getCause();
+ if (currentEx != null) {
+ s.startTag("", "line");
+ try {
+ s.attribute("", "file", "### CAUSED BY ###: "
+ + currentEx.toString());
+ s.attribute("", "number", "");
+ } catch (Throwable ex) {
+ Log.v(LOG_TAG, "Exception caught:", ex);
+ }
+ s.endTag("", "line");
+ }
+ } catch (Throwable innerException) {
+ Log.v(LOG_TAG, "Exception caught:", e);
+ break;
+ }
+ }
+ s.endTag("", "backtrace");
+ s.endTag("", "error");
+
+ // Additional request info
+ s.startTag("", "request");
+
+ s.startTag("", "url");
+ s.endTag("", "url");
+ s.startTag("", "component");
+ s.endTag("", "component");
+ s.startTag("", "action");
+ s.endTag("", "action");
+
+ // write envirnonment data
+ s.startTag("", "cgi-data");
+
+ writeValue(s, "Manufacturer", manufacturerDevice);
+ writeValue(s, "Device", phoneModel);
+ writeValue(s, "Brand", brandDevice);
+ writeValue(s, "Android Version", androidVersion);
+ writeValue(s, "App Version", versionName);
+
+ // use default resources instead
+ if (res == null || res.getConfiguration() == null) {
+ res = defaultResources;
+ }
+
+ if (res != null && res.getConfiguration() != null) {
+ // write current android configurations to environment
+ Configuration conf = res.getConfiguration();
+ // writeValue(s, "densityDpi", conf.densityDpi);
+ writeValue(s, "fontScale", conf.fontScale);
+ writeValue(s, "hardKeyboardHidden", conf.hardKeyboardHidden);
+ writeValue(s, "keyboard", conf.keyboard);
+ writeValue(s, "keyboardHidden", conf.keyboardHidden);
+ writeValue(s, "locale", conf.locale);
+ writeValue(s, "mcc", conf.mcc);
+ writeValue(s, "mnc", conf.mnc);
+ writeValue(s, "navigation", conf.navigation);
+ writeValue(s, "navigationHidden", conf.navigationHidden);
+ writeValue(s, "orientation", conf.orientation);
+ writeValue(s, "screenHeightDp", conf.screenHeightDp);
+ writeValue(s, "screenLayout", conf.screenLayout);
+ writeValue(s, "screenWidthDp", conf.screenWidthDp);
+ writeValue(s, "smallestScreenWidthDp",
+ conf.smallestScreenWidthDp);
+ writeValue(s, "touchscreen", conf.touchscreen);
+ writeValue(s, "uiMode", conf.uiMode);
+ }
+
+ s.endTag("", "cgi-data");
+
+ // write session data
+ s.startTag("", "session");
+ for (Entry entry : sessionData.entrySet()) {
+ writeValue(s, entry.getKey(), entry.getValue());
+ }
+ s.endTag("", "session");
+
+ // write request data
+ s.startTag("", "params");
+
+ for (Entry entry : requestData.entrySet()) {
+ writeValue(s, entry.getKey(), entry.getValue());
+ }
+
+ // Extra info, if present
+ if (environmentData != null && !environmentData.isEmpty()) {
+ for (Map.Entry extra : environmentData
+ .entrySet()) {
+ s.startTag("", "var");
+ s.attribute("", "key", extra.getKey());
+ s.text(extra.getValue());
+ s.endTag("", "var");
+ }
+ }
+
+ s.endTag("", "params");
+
+ // Metadata, if present
+ if (metaData != null && !metaData.isEmpty()) {
+ for (Map.Entry extra : metaData.entrySet()) {
+ s.startTag("", "var");
+ s.attribute("", "key", extra.getKey());
+ s.text(extra.getValue());
+ s.endTag("", "var");
+ }
+ }
+
+ s.endTag("", "request");
+
+ // Production/development mode flag and app version
+ s.startTag("", "server-environment");
+ s.startTag("", "environment-name");
+ s.text(environmentName);
+ s.endTag("", "environment-name");
+ s.startTag("", "app-version");
+ s.text(versionName);
+ s.endTag("", "app-version");
+ s.endTag("", "server-environment");
+
+ // Close document
+ s.endTag("", "notice");
+ s.endDocument();
+
+ // Flush to disk
+ writer.flush();
+ writer.close();
+
+ Log.d(LOG_TAG, "Writing new " + e.getClass().getName()
+ + " exception to disk.");
+ } catch (Exception ex) {
+ Log.v(LOG_TAG, "Exception caught:", ex);
+ }
+ }
+
+ private static void writeValue(XmlSerializer s, String key, int value)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ writeValue(s, key, String.valueOf(value));
+ }
+
+ private static void writeValue(XmlSerializer s, String key, Object value)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ writeValue(s, key, (value == null) ? "" : value.toString());
+ }
+
+ private static void writeValue(XmlSerializer s, String key, String value)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ s.startTag("", "var");
+ s.attribute("", "key", key);
+ s.text(value);
+ s.endTag("", "var");
+ }
+
+ private static void sendExceptionData(File file) {
+ try {
+ boolean sent = false;
+ URL url = new URL(errbit_endpoint);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ try {
+ // Set up the connection
+ conn.setDoOutput(true);
+ conn.setRequestProperty("Content-Type",
+ "text/xml; charset=utf-8");
+
+ // Read from the file and send it
+ FileInputStream is = new FileInputStream(file);
+ OutputStream os = conn.getOutputStream();
+ byte[] buffer = new byte[4096];
+ int numRead;
+ while ((numRead = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, numRead);
+ }
+ os.flush();
+ os.close();
+ is.close();
+
+ // Flush the request through
+ int response = conn.getResponseCode();
+ Log.d(LOG_TAG,
+ "Sent exception file " + file.getName()
+ + " to Errbit. Got response code "
+ + String.valueOf(response));
+
+ sent = true;
+
+ } catch (IOException e) {
+
+ Log.v(LOG_TAG, "Exception caught:", e);
+
+ } finally {
+
+ // delete the file only if sending was successful
+ if (sent) {
+ file.delete();
+ }
+
+ conn.disconnect();
+ }
+
+ } catch (Exception e) {
+
+ // unknown exception
+ Log.v(LOG_TAG, "Exception caught:", e);
+
+ }
+ }
+
+ private static synchronized void flushExceptions() {
+ File exceptionDir = new File(filePath);
+ if (exceptionDir.exists() && exceptionDir.isDirectory()) {
+ File[] exceptions = exceptionDir.listFiles();
+ for (File f : exceptions) {
+ if (f.exists() && f.isFile()) {
+ sendExceptionData(f);
+ }
+ }
+ }
+ }
}
\ No newline at end of file