package com.newsrob;
import java.io.File;
import java.io.FileInputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Debug;
import android.util.Log;
import com.newsrob.activities.ShowMessageActivity;
import com.newsrob.util.NewsRobStrictMode;
import com.newsrob.util.SDKVersionUtil;
import com.newsrob.util.U;
public class NewsRob extends Application {
private static final String TAG = NewsRob.class.getSimpleName();
public static final boolean SHOW_CHANGED = true;
private static final String DEBUG_PREFERENCES_FILE = "/sdcard/newsrob.debug";
final static String HEAP_DUMP_FILE_NAME = "/sdcard/newsrob/newsrob.hprof";
private static Properties debugProperties;
private static Boolean debuggingEnabled;
public static final String CONSTANT_MY_RECENTLY_STARRED = "my recently starred";
public static final String CONSTANT_FRIENDS_RECENTLY_SHARED = "friends' recently shared";
public static Activity lastActivity;
public static boolean fireModelUpdateInProgress;
private static final boolean STRICT_MODE_ENABLED = false;
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "NewsRob.onCreate()");
installNewsRobDefaultExceptionHandler(this);
enableStrictMode();
// remove old heap dump after one week
final File f = new File(HEAP_DUMP_FILE_NAME);
if (f.exists() && f.lastModified() < (System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000)))
f.delete();
// setTheme(getEntryManager().getThemeResourceId());
// re-schedule syncs after app has been (re-)installed
final EntryManager entryManager = EntryManager.getInstance(this);
entryManager.getScheduler().ensureSchedulingIsEnabled();
entryManager.maintainBootReceiverStateAndScheduler();
entryManager.maintainPremiumDependencies();
entryManager.maintainFirstInstalledVersion();
entryManager.migrateSyncType();
}
private void enableStrictMode() {
if (STRICT_MODE_ENABLED && SDKVersionUtil.getVersion() >= 9)
NewsRobStrictMode.enableStrictMode();
}
public static void installNewsRobDefaultExceptionHandler(final NewsRob context) {
final boolean isNRExceptionHandlerAlreadyInstalled = NewsRobDefaultExceptionHandler.class.getSimpleName()
.equals(Thread.getDefaultUncaughtExceptionHandler().getClass().getSimpleName());
Log.d(TAG, "NewsRob Default Exception handler installed already? " + isNRExceptionHandlerAlreadyInstalled);
if (!isNRExceptionHandlerAlreadyInstalled)
new NewsRobDefaultExceptionHandler(context);
}
@Override
public void onTerminate() {
Log.d(TAG, "NewsRob.onTerminate()");
super.onTerminate();
}
public static Properties getDebugProperties(Context context) {
if (debugProperties == null) {
synchronized (NewsRob.class) {
debugProperties = new Properties();
try {
debugProperties.load(new FileInputStream(DEBUG_PREFERENCES_FILE));
Log.i(TAG, "Debug properties loaded: " + debugProperties.toString());
} catch (final Exception e) {
Log.w(TAG, "No debug properties loaded.");
}
}
}
return debugProperties;
}
static class NewsRobDefaultExceptionHandler implements Thread.UncaughtExceptionHandler {
private final UncaughtExceptionHandler oldDefaultExceptionHandler;
private final NewsRob context;
private static final String TAG = "NewsRobDefaultExceptionHandler";
NewsRobDefaultExceptionHandler(final NewsRob context) {
this.context = context;
Log.d(TAG, "Default Exception Handler=" + Thread.getDefaultUncaughtExceptionHandler());
oldDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
Log.d(TAG, "Installed Exception Handler=" + Thread.getDefaultUncaughtExceptionHandler());
}
public void uncaughtException(final Thread t, final Throwable e) {
Log.e("NewsRob", "Caught the following exception: ", e);
final StringBuilder message = new StringBuilder(
"Sorry!\n\nNewsRob hit a wall. Please send this mail, so that the developer can analyze/fix the issue.\nIf it is not too much to ask, please add to this mail what you just did between the following lines:\n\n-------\n\n\n-------\n");
SettingsRenderer.renderSettings(EntryManager.getInstance(context), message);
if (lastActivity != null)
message.append("-- LastActivity: " + lastActivity.getClass().getSimpleName() + ".\n");
/*
* StringBuilder sb = new StringBuilder("-- Networks: ("); try {
* ConnectivityManager cm = (ConnectivityManager) context
* .getSystemService(CONNECTIVITY_SERVICE);
*
* for (NetworkInfo ni : cm.getAllNetworkInfo()) { sb.append("\n "
* + ni.getTypeName() + ": " + ni.getDetailedState() + " - " +
* ni.getSubtypeName()); }
*
*
* } finally { sb.append(")\n"); message.append(sb); }
*/
U.renderStackTrace(e, message);
final String messageBody = message.toString();
// create heap dump if OOM is in stacktrace
// boolean heapDumpCreated = createHeapDump(messageBody);
if (Pattern.compile("OutOfMemoryError").matcher(messageBody).find()) {
createHeapDump(messageBody);
final Intent showMessageIntent = new Intent(context, ShowMessageActivity.class);
showMessageIntent.putExtra("title", "Out Of Memory");
showMessageIntent.putExtra("body", "" + context.getText(R.string.oom_message));
showMessageIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(showMessageIntent);
if (oldDefaultExceptionHandler != null)
oldDefaultExceptionHandler.uncaughtException(t, e);
return;
}
List<String> ignorePatterns = new ArrayList<String>(10);
// Flash Player
ignorePatterns.add("java.lang.UnsatisfiedLinkError: nativeIsOpenGLEnabled");
// http://code.google.com/p/android/issues/detail?id=10041
// Data Loader NPE
ignorePatterns.add("WebViewCore.java:636");
ignorePatterns.add("WebViewCore.java:629");
ignorePatterns.add("WebViewCore.java:612");
ignorePatterns.add("WebViewCore.java:611");
ignorePatterns.add("WebViewCore.java:622");
// Ads related exceptions
ignorePatterns.add("com.google.ads");
ignorePatterns.add("com.admob");
// internal WebView issue
ignorePatterns.add("android.webkit.WebViewDatabase.clearCache");
// SSL certificate issue
ignorePatterns.add("no more than 32 elements");
// LG issue
ignorePatterns.add("android.permission.WRITE_SETTINGS");
// ignore a known issue:
// http://code.google.com/p/android/issues/detail?id=2275
ignorePatterns.add("CacheManager.java:391");
// ignore bad token exceptions
ignorePatterns.add("android.view.WindowManager\\$BadTokenException");
// ignore WebView 1.6 bug
ignorePatterns.add("Null or empty value for header");
// WebView NPE
ignorePatterns.add("SurfaceView.java:547");
// Android HTTP Client
ignorePatterns.add("Connection.java:231");
// WebView NPE in handleMessage
// http://code.google.com/p/android/issues/detail?id=9995
ignorePatterns.add("WebView.java:7590");
ignorePatterns.add("WebView.java:7577");
ignorePatterns.add("WebView.java:6384");
ignorePatterns.add("WebView.java:7779");
ignorePatterns.add("WebView.java:7612");
ignorePatterns.add("WebView.java:7850");
ignorePatterns.add("WebView.java:7617");
// WebView
// http://code.google.com/p/android/issues/detail?id=11583
ignorePatterns.add("BasicHttpRequest.java:57");
// WebView COMMIT exception in CacheManager.endTransaction()
ignorePatterns.add("CacheManager.java:276");
// Android HTTP Client
ignorePatterns.add("CharArrayBuffer.java:125");
//
ignorePatterns.add("WebViewDatabase.java:736");
// http://code.google.com/p/android/issues/detail?id=11977
ignorePatterns.add("WebViewDatabase.java:737");
// http://code.google.com/p/android/issues/detail?id=12236
ignorePatterns.add("WebViewCore.java:1218");
ignorePatterns.add("WebViewCore.java:691");
ignorePatterns.add("WebViewCore.java:635");
for (String ignorePattern : ignorePatterns)
if (Pattern.compile(ignorePattern).matcher(messageBody).find()) {
Log.d("NewsRob ErrorHandler", "Ignoring: " + messageBody);
return;
}
// redirect to a website when permissions are broken
if (Pattern.compile("process has android.permission.WAKE_LOCK").matcher(messageBody).find()
|| Pattern.compile("process has android.permission.ACCESS_NETWORK_STATE").matcher(messageBody)
.find()
|| Pattern.compile("lacks android.permission.USE_CREDENTIALS").matcher(messageBody).find()) {
final Uri uri = Uri
.parse("http://newsrob.blogspot.com/2009/09/broken-permissions-due-to-rooting-your.html");
context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
return;
}
// Don't report bugs in Custom ROMs anymore
if ("Stock Android".equals(SettingsRenderer.getCustomRomVersion())) {
// Prepare Mail
final Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sendIntent.setType("message/rfc822");
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { "bugs.newsrob@gmail.com" });
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "BugReport: " + e.getClass().getSimpleName() + ": "
+ e.getMessage());
sendIntent.putExtra(Intent.EXTRA_TEXT, messageBody);
Log.d(TAG, "Message Body: " + messageBody);
// if (heapDumpCreated)
// sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://"
// + HEAP_DUMP_FILE_NAME));
Log.e(TAG, "Exception handled. Email activity should be initiated now.");
// Send Mail
new Thread(new Runnable() {
public void run() {
context.startActivity(sendIntent);
}
}).start();
Log.e(TAG, "Exception handled. Email should be sent by now.");
}
// Use default exception mechanism
if (oldDefaultExceptionHandler != null)
oldDefaultExceptionHandler.uncaughtException(t, e);
}
private boolean createHeapDump(final String messageBody) {
if (Pattern.compile("OutOfMemoryError").matcher(messageBody).find()) {
try {
final Method m = Debug.class.getMethod("dumpHprofData", new Class[] { String.class });
m.invoke(null, HEAP_DUMP_FILE_NAME);
return true;
} catch (final Throwable throwable) {
Log.d(TAG, "Error writing hprof dump.", throwable);
}
}
return false;
}
}
public static boolean isDebuggingEnabled(Context context) {
if (debuggingEnabled == null)
debuggingEnabled = "1".equals(getDebugProperties(context).getProperty("debug", "0"));
return debuggingEnabled;
}
public static void sendLogFile(Context context) {
}
}