diff --git a/res/values/databases.xml b/res/values/databases.xml index a6d7f01..672f1cd 100755 --- a/res/values/databases.xml +++ b/res/values/databases.xml @@ -12,4 +12,8 @@ CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, direction TEXT, length INTEGER, body TEXT, number_name TEXT, number TEXT); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, event_name TEXT, event_details TEXT); + \ No newline at end of file diff --git a/src/com/audacious_software/passive_data_kit/Logger.java b/src/com/audacious_software/passive_data_kit/Logger.java index b315574..9ea620e 100755 --- a/src/com/audacious_software/passive_data_kit/Logger.java +++ b/src/com/audacious_software/passive_data_kit/Logger.java @@ -2,7 +2,10 @@ import android.content.Context; +import com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent; + import java.util.HashMap; +import java.util.Map; /** * Created by cjkarr on 4/3/2016. @@ -10,8 +13,12 @@ public class Logger { private Context mContext = null; - public void log(String event, HashMap details) { - // TODO + public void log(String event, Map details) { + if (details == null) { + details = new HashMap<>(); + } + + AppEvent.getInstance(this.mContext).logEvent(event, details); } private static class LoggerHolder { diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index d33c9f4..14a4d26 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -11,6 +11,7 @@ import com.audacious_software.passive_data_kit.Logger; import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent; import com.audacious_software.pdk.passivedatakit.R; import java.lang.reflect.InvocationTargetException; @@ -37,6 +38,8 @@ public void start() { { this.mGenerators.clear(); + this.mGenerators.add(AppEvent.class.getCanonicalName()); + for (String className : this.mContext.getResources().getStringArray(R.array.pdk_available_generators)) { this.mGenerators.add(className); diff --git a/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java new file mode 100755 index 0000000..c8d6001 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java @@ -0,0 +1,436 @@ +package com.audacious_software.passive_data_kit.generators.diagnostics; + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.audacious_software.passive_data_kit.PassiveDataKit; +import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.passive_data_kit.generators.Generators; +import com.audacious_software.pdk.passivedatakit.R; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AppEvent extends Generator{ + private static final String GENERATOR_IDENTIFIER = "pdk-app-event"; + + private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent.ENABLED"; + private static final boolean ENABLED_DEFAULT = true; + + private static final String DATABASE_PATH = "pdk-app-event.sqlite"; + private static final int DATABASE_VERSION = 1; + + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_EVENT_NAME = "event_name"; + public static final String HISTORY_EVENT_DETAILS = "event_details"; + public static final String TABLE_HISTORY = "history"; + + private static AppEvent sInstance = null; + + private SQLiteDatabase mDatabase = null; + + public static AppEvent getInstance(Context context) { + if (AppEvent.sInstance == null) { + AppEvent.sInstance = new AppEvent(context.getApplicationContext()); + } + + return AppEvent.sInstance; + } + + public AppEvent(Context context) { + super(context); + } + + public static void start(final Context context) { + AppEvent.getInstance(context).startGenerator(); + } + + private void startGenerator() { + final AppEvent me = this; + + Generators.getInstance(this.mContext).registerCustomViewClass(AppEvent.GENERATOR_IDENTIFIER, AppEvent.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, AppEvent.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_app_events_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, AppEvent.DATABASE_VERSION); + } + + public static boolean isEnabled(Context context) { + SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); + + return prefs.getBoolean(AppEvent.ENABLED, AppEvent.ENABLED_DEFAULT); + } + + public static boolean isRunning(Context context) { + return (AppEvent.sInstance != null); + } + + public static ArrayList diagnostics(Context context) { + return new ArrayList<>(); + } + + public static void bindViewHolder(DataPointViewHolder holder) { +/* final Context context = holder.itemView.getContext(); + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + long zeroStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); + + LinearLayout zeroTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_zero_value); + + AppEvents.populateTimeline(context, zeroTimeline, zeroStart); + + long oneStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); + + LinearLayout oneTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_one_value); + + AppEvents.populateTimeline(context, oneTimeline, oneStart); + + long twoStart = cal.getTimeInMillis(); + + LinearLayout twoTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_two_value); + + AppEvents.populateTimeline(context, twoTimeline, twoStart); + + AppEvents generator = AppEvents.getInstance(context); + + Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, null, null, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); + + View cardContent = holder.itemView.findViewById(R.id.card_content); + View cardEmpty = holder.itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); + + if (c.moveToNext()) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); + + long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)) / 1000; + + dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + + cal = Calendar.getInstance(); + DateFormat format = android.text.format.DateFormat.getDateFormat(context); + + TextView zeroDayLabel = (TextView) holder.itemView.findViewById(R.id.day_zero_label); + zeroDayLabel.setText(format.format(cal.getTime())); + + cal.add(Calendar.DATE, -1); + + TextView oneDayLabel = (TextView) holder.itemView.findViewById(R.id.day_one_label); + oneDayLabel.setText(format.format(cal.getTime())); + + cal.add(Calendar.DATE, -1); + + TextView twoDayLabel = (TextView) holder.itemView.findViewById(R.id.day_two_label); + twoDayLabel.setText(format.format(cal.getTime())); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + + c.close(); + */ + } + + /* + private static void populateTimeline(Context context, LinearLayout timeline, long start) { + timeline.removeAllViews(); + + AppEvents generator = AppEvents.getInstance(context); + + long end = start + (24 * 60 * 60 * 1000); + + String where = AppEvents.HISTORY_OBSERVED + " >= ? AND " + AppEvents.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start, "" + end }; + + Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, where, args, null, null, AppEvents.HISTORY_OBSERVED); + + ArrayList activeStates = new ArrayList<>(); + ArrayList activeTimestamps = new ArrayList<>(); + + while (c.moveToNext()) { + long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)); + + activeTimestamps.add(timestamp); + + String state = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); + activeStates.add(state); + } + + c.close(); + + String lastState = AppEvents.STATE_UNKNOWN; + + String lastWhere = AppEvents.HISTORY_OBSERVED + " < ?"; + String[] lastArgs = { "" + start }; + + c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, lastWhere, lastArgs, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + lastState = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); + } + + if (activeStates.size() > 0) { + long firstTimestamp = activeTimestamps.get(0); + long firstState = activeTimestamps.get(0); + + View startView = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_UNKNOWN.equals(lastState)) { + + } else if (AppEvents.STATE_ON.equals(lastState)) { + startView.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(lastState)) { + startView.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(lastState)) { + startView.setBackgroundColor(0xff1b5e20); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(lastState)) { + startView.setBackgroundColor(0xff1b5e20); + } + } else { + if (AppEvents.STATE_UNKNOWN.equals(lastState)) { + + } else if (AppEvents.STATE_ON.equals(lastState)) { + startView.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(lastState)) { + startView.setBackgroundColor(0xff263238); + } + } + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, firstTimestamp - start); + startView.setLayoutParams(params); + + timeline.addView(startView); + + long now = System.currentTimeMillis(); + + if (activeStates.size() == 1) { + View v = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_ON.equals(firstState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(firstState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(firstState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(firstState)) { + v.setBackgroundColor(0xff3f51b5); + } + } else { + if (AppEvents.STATE_ON.equals(firstState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(firstState)) { + v.setBackgroundColor(0xff263238); + } + } + + if (end > System.currentTimeMillis()) { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - firstTimestamp); + v.setLayoutParams(params); + } else { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - firstTimestamp); + v.setLayoutParams(params); + } + + timeline.addView(v); + } else { + for (int i = 1; i < activeStates.size(); i++) { + long currentTimestamp = activeTimestamps.get(i); + + long priorTimestamp = activeTimestamps.get(i - 1); + String priorState = activeStates.get(i - 1); + + View v = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_ON.equals(priorState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(priorState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(priorState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(priorState)) { + v.setBackgroundColor(0xff3f51b5); + } + } else { + if (AppEvents.STATE_ON.equals(priorState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(priorState)) { + v.setBackgroundColor(0xff263238); + } + } + + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, currentTimestamp - priorTimestamp); + v.setLayoutParams(params); + + timeline.addView(v); + } + + long finalTimestamp = activeTimestamps.get(activeTimestamps.size() - 1); + String finalState = activeStates.get(activeStates.size() - 1); + + View v = new View(context); + + if (AppEvents.STATE_ON.equals(finalState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(finalState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(finalState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(finalState)) { + v.setBackgroundColor(0xff3f51b5); + } + + if (end > now) { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - finalTimestamp); + v.setLayoutParams(params); + } else { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - finalTimestamp); + v.setLayoutParams(params); + } + + timeline.addView(v); + } + + if (end > now) { + View v = new View(context); + + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - now); + v.setLayoutParams(params); + + timeline.addView(v); + } + } else { + + } + } + +*/ + public static View fetchView(ViewGroup parent) + { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_generic, parent, false); + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static long latestPointGenerated(Context context) { + long timestamp = 0; + + AppEvent me = AppEvent.getInstance(context); + + Cursor c = me.mDatabase.query(AppEvent.TABLE_HISTORY, null, null, null, null, null, AppEvent.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(AppEvent.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; + } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(AppEvent.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } + + public boolean logEvent(String eventName, Map eventDetails) { + try { + long now = System.currentTimeMillis(); + + ContentValues values = new ContentValues(); + values.put(AppEvent.HISTORY_OBSERVED, now); + values.put(AppEvent.HISTORY_EVENT_NAME, eventName); + + Bundle detailsBundle = new Bundle(); + JSONObject detailsJson = new JSONObject(); + + for (String key : eventDetails.keySet()) { + Object value = eventDetails.get(key); + + if (value instanceof Double) { + Double doubleValue = ((Double) value); + + detailsBundle.putDouble(key, doubleValue); + detailsJson.put(key, doubleValue.doubleValue()); + } else if (value instanceof Float) { + Float floatValue = ((Float) value); + + detailsBundle.putDouble(key, floatValue.doubleValue()); + detailsJson.put(key, floatValue.doubleValue()); + } else if (value instanceof Long) { + Long longValue = ((Long) value); + + detailsBundle.putLong(key, longValue.longValue()); + detailsJson.put(key, longValue.longValue()); + } else if (value instanceof Integer) { + Integer intValue = ((Integer) value); + + detailsBundle.putLong(key, intValue.longValue()); + detailsJson.put(key, intValue.longValue()); + } else if (value instanceof String) { + detailsBundle.putString(key, value.toString()); + detailsJson.put(key, value.toString()); + } else { + detailsBundle.putString(key, "Unknown Class: " + value.getClass().getCanonicalName()); + detailsJson.put(key, "Unknown Class: " + value.getClass().getCanonicalName()); + } + } + + values.put(AppEvent.HISTORY_EVENT_DETAILS, detailsJson.toString(2)); + + this.mDatabase.insert(AppEvent.TABLE_HISTORY, null, values); + + Bundle update = new Bundle(); + update.putLong(AppEvent.HISTORY_OBSERVED, now); + update.putString(AppEvent.HISTORY_EVENT_NAME, values.getAsString(AppEvent.HISTORY_EVENT_NAME)); + update.putBundle(AppEvent.HISTORY_EVENT_DETAILS, detailsBundle); + + Generators.getInstance(this.mContext).notifyGeneratorUpdated(AppEvent.GENERATOR_IDENTIFIER, update); + + return true; + } catch (JSONException e) { + e.printStackTrace(); + } + + return false; + } +}