package com.eveningoutpost.dexdrip.Models; import android.os.AsyncTask; import android.provider.BaseColumns; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Home; import com.eveningoutpost.dexdrip.UtilityModels.Pref; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.annotations.Expose; import com.google.gson.internal.bind.DateTypeAdapter; import java.util.Date; import java.util.Hashtable; import java.util.List; /** * Created by Emma Black on 8/3/15. */ @Table(name = "UserErrors", id = BaseColumns._ID) public class UserError extends PlusModel { private final static String TAG = UserError.class.getSimpleName(); private static boolean patched = false; private static final String schema[] = { "CREATE TABLE UserErrors (_id INTEGER PRIMARY KEY AUTOINCREMENT, message TEXT, severity INTEGER, shortError TEXT, timestamp REAL)", "CREATE INDEX index_UserErrors_timestamp on UserErrors(timestamp)", "CREATE INDEX index_UserErrors_severity on UserErrors(severity)" }; @Expose @Column(name = "shortError") public String shortError; // Short error message to be displayed on table @Expose @Column(name = "message") public String message; // Additional text when error is expanded @Expose @Column(name = "severity", index = true) public int severity; // int between 1 and 3, 3 being most severe // 5 = internal lower level user events // 6 = higher granularity user events @Expose @Column(name = "timestamp", index = true) public double timestamp; // Time the error was raised //todo: rather than include multiples of the same error, should we have a "Count" and just increase that on duplicates? //or rather, perhaps we should group up the errors public String toString() { return severity+" ^ "+JoH.dateTimeText((long)timestamp)+" ^ "+shortError+" ^ "+message; } public UserError() { super (); } public UserError(int severity, String shortError, String message) { this.severity = severity; this.shortError = shortError; this.message = message; this.timestamp = new Date().getTime(); this.save(); } public UserError(String shortError, String message) { this(2, shortError, message); } public static UserError UserErrorHigh(String shortError, String message) { return new UserError(3, shortError, message); } public static UserError UserErrorLow(String shortError, String message) { return new UserError(1, shortError, message); } public static UserError UserEventLow(String shortError, String message) { return new UserError(5, shortError, message); } public static UserError UserEventHigh(String shortError, String message) { return new UserError(6, shortError, message); } public static void cleanup() { new Cleanup().execute(deletable()); } public static void cleanup(long timestamp) { patched = fixUpTable(schema, patched); List<UserError> userErrors = new Select() .from(UserError.class) .where("timestamp < ?", timestamp) .orderBy("timestamp desc") .execute(); if (userErrors != null) Log.d(TAG, "cleanup UserError size=" + userErrors.size()); new Cleanup().execute(userErrors); } public static List<UserError> all() { return new Select() .from(UserError.class) .orderBy("timestamp desc") .execute(); } public static List<UserError> deletable() { List<UserError> userErrors = new Select() .from(UserError.class) .where("severity < ?", 3) .where("timestamp < ?", (new Date().getTime() - 1000 * 60 * 60 * 24)) .orderBy("timestamp desc") .execute(); List<UserError> highErrors = new Select() .from(UserError.class) .where("severity = ?", 3) .where("timestamp < ?", (new Date().getTime() - 1000*60*60*24*3)) .orderBy("timestamp desc") .execute(); List<UserError> events = new Select() .from(UserError.class) .where("severity > ?", 3) .where("timestamp < ?", (new Date().getTime() - 1000*60*60*24*7)) .orderBy("timestamp desc") .execute(); userErrors.addAll(highErrors); userErrors.addAll(events); return userErrors; } public static List<UserError> bySeverity(Integer[] levels) { String levelsString = " "; for (int level : levels) { levelsString += level + ","; } Log.d("UserError", "severity in ("+levelsString.substring(0,levelsString.length() - 1)+")"); return new Select() .from(UserError.class) .where("severity in ("+levelsString.substring(0,levelsString.length() - 1)+")") .orderBy("timestamp desc") .execute(); } public static UserError last() { return new Select() .from(UserError.class) .orderBy("_ID desc") .executeSingle(); } public static List<UserError> latestAsc(int number, long startTime) { return latestAsc(number, startTime, Long.MAX_VALUE); } public static List<UserError> latestAsc(int number, long startTime, long endTime) { return new Select() .from(UserError.class) .where("timestamp >= " + Math.max(startTime, 0)) .where("timestamp <= " + endTime) .orderBy("timestamp asc") .limit(number) .execute(); } public String toS() { Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapter(Date.class, new DateTypeAdapter()) .serializeSpecialFloatingPointValues() .create(); return gson.toJson(this); } public static UserError getForTimestamp(UserError error) { try { return new Select() .from(UserError.class) .where("timestamp = ?", error.timestamp) .where("shortError = ?", error.shortError) .where("message = ?", error.message) .executeSingle(); } catch (Exception e) { Log.e(TAG,"getForTimestamp() Got exception on Select : "+e.toString()); return null; } } private static class Cleanup extends AsyncTask<List<UserError>, Integer, Boolean> { @Override protected Boolean doInBackground(List<UserError>... errors) { try { for(UserError userError : errors[0]) { userError.delete(); //userError.save(); } return true; } catch(Exception e) { return false; } } } public static List<UserError> bySeverity(int level) { return bySeverity(new Integer[]{level}); } public static List<UserError> bySeverity(int level, int level2) { return bySeverity(new Integer[]{ level, level2 }); } public static List<UserError> bySeverity(int level, int level2, int level3) { return bySeverity(new Integer[]{ level, level2, level3 }); } public static class Log { public static void e(String a, String b){ android.util.Log.e(a, b); new UserError(a, b); } public static void e(String tag, String b, Exception e){ android.util.Log.e(tag, b, e); new UserError(tag, b + "\n" + e.toString()); } public static void w(String tag, String b){ android.util.Log.w(tag, b); UserError.UserErrorLow(tag, b); } public static void w(String tag, String b, Exception e){ android.util.Log.w(tag, b, e); UserError.UserErrorLow(tag, b + "\n" + e.toString()); } public static void wtf(String tag, String b){ android.util.Log.wtf(tag, b); UserError.UserErrorHigh(tag, b); } public static void wtf(String tag, String b, Exception e){ android.util.Log.wtf(tag, b, e); UserError.UserErrorHigh(tag, b + "\n" + e.toString()); } public static void wtf(String tag, Exception e){ android.util.Log.wtf(tag, e); UserError.UserErrorHigh(tag, e.toString()); } public static void uel(String tag, String b) { android.util.Log.i(tag, b); UserError.UserEventLow(tag, b); } public static void ueh(String tag, String b) { android.util.Log.i(tag, b); UserError.UserEventHigh(tag, b); } public static void d(String tag, String b){ android.util.Log.d(tag, b); if(ExtraLogTags.shouldLogTag(tag, android.util.Log.DEBUG)) { UserErrorLow(tag, b); } } public static void v(String tag, String b){ android.util.Log.v(tag, b); if(ExtraLogTags.shouldLogTag(tag, android.util.Log.VERBOSE)) { UserErrorLow(tag, b); } } public static void i(String tag, String b){ android.util.Log.i(tag, b); if(ExtraLogTags.shouldLogTag(tag, android.util.Log.INFO)) { UserErrorLow(tag, b); } } static ExtraLogTags extraLogTags = new ExtraLogTags(); } public static class ExtraLogTags { static Hashtable <String, Integer> extraTags; ExtraLogTags () { extraTags = new Hashtable <String, Integer>(); String extraLogs = Pref.getStringDefaultBlank("extra_tags_for_logging"); readPreference(extraLogs); } /* * This function reads a string representing tags that the user wants to log * Format of string is tag1:level1,tag2,level2 * Example of string is Alerts:i,BG:W * */ public static void readPreference(String extraLogs) { extraLogs = extraLogs.trim(); if (extraLogs.length() > 0) UserErrorLow(TAG, "called with string " + extraLogs); extraTags.clear(); // allow splitting to work with a single entry and no delimiter zzz if ((extraLogs.length() > 1) && (!extraLogs.contains(","))) { extraLogs += ","; } String[] tags = extraLogs.split(","); if (tags.length == 0) { return; } // go over all tags and parse them for(String tag : tags) { if (tag.length() > 0) parseTag(tag); } } static void parseTag(String tag) { // Format is tag:level for example Alerts:i String[] tagAndLevel = tag.trim().split(":"); if(tagAndLevel.length != 2) { Log.e(TAG, "Failed to parse " + tag); return; } String level = tagAndLevel[1]; String tagName = tagAndLevel[0].toLowerCase(); if (level.compareTo("d") == 0) { extraTags.put(tagName, android.util.Log.DEBUG); UserErrorLow(TAG, "Adding tag with DEBUG " + tagAndLevel[0] ); return; } if (level.compareTo("v") == 0) { extraTags.put(tagName, android.util.Log.VERBOSE); UserErrorLow(TAG,"Adding tag with VERBOSE " + tagAndLevel[0] ); return; } if (level.compareTo("i") == 0) { extraTags.put(tagName, android.util.Log.INFO); UserErrorLow(TAG, "Adding tag with info " + tagAndLevel[0] ); return; } Log.e(TAG, "Unknown level for tag " + tag + " please use d v or i"); } static boolean shouldLogTag(String tag, int level) { Integer levelForTag = extraTags.get(tag.toLowerCase()); return levelForTag != null && level >= levelForTag; } } }