diff --git a/res/drawable-hdpi/ic_ambient_light.png b/res/drawable-hdpi/ic_ambient_light.png new file mode 100755 index 0000000..01cd42f Binary files /dev/null and b/res/drawable-hdpi/ic_ambient_light.png differ diff --git a/res/drawable-hdpi/ic_pdk_accelerometer.png b/res/drawable-hdpi/ic_pdk_accelerometer.png new file mode 100755 index 0000000..0fdc766 Binary files /dev/null and b/res/drawable-hdpi/ic_pdk_accelerometer.png differ diff --git a/res/drawable-hdpi/ic_pdk_action_lock.png b/res/drawable-hdpi/ic_pdk_action_lock.png new file mode 100755 index 0000000..6409845 Binary files /dev/null and b/res/drawable-hdpi/ic_pdk_action_lock.png differ diff --git a/res/drawable-hdpi/ic_pdk_action_unlock.png b/res/drawable-hdpi/ic_pdk_action_unlock.png new file mode 100755 index 0000000..f5a7c05 Binary files /dev/null and b/res/drawable-hdpi/ic_pdk_action_unlock.png differ diff --git a/res/drawable-mdpi/ic_ambient_light.png b/res/drawable-mdpi/ic_ambient_light.png new file mode 100755 index 0000000..183e053 Binary files /dev/null and b/res/drawable-mdpi/ic_ambient_light.png differ diff --git a/res/drawable-mdpi/ic_pdk_accelerometer.png b/res/drawable-mdpi/ic_pdk_accelerometer.png new file mode 100755 index 0000000..5570049 Binary files /dev/null and b/res/drawable-mdpi/ic_pdk_accelerometer.png differ diff --git a/res/drawable-mdpi/ic_pdk_action_lock.png b/res/drawable-mdpi/ic_pdk_action_lock.png new file mode 100755 index 0000000..53377c1 Binary files /dev/null and b/res/drawable-mdpi/ic_pdk_action_lock.png differ diff --git a/res/drawable-mdpi/ic_pdk_action_unlock.png b/res/drawable-mdpi/ic_pdk_action_unlock.png new file mode 100755 index 0000000..542703d Binary files /dev/null and b/res/drawable-mdpi/ic_pdk_action_unlock.png differ diff --git a/res/drawable-xhdpi/ic_ambient_light.png b/res/drawable-xhdpi/ic_ambient_light.png new file mode 100755 index 0000000..64185f1 Binary files /dev/null and b/res/drawable-xhdpi/ic_ambient_light.png differ diff --git a/res/drawable-xhdpi/ic_pdk_accelerometer.png b/res/drawable-xhdpi/ic_pdk_accelerometer.png new file mode 100755 index 0000000..b5cdeaf Binary files /dev/null and b/res/drawable-xhdpi/ic_pdk_accelerometer.png differ diff --git a/res/drawable-xhdpi/ic_pdk_action_lock.png b/res/drawable-xhdpi/ic_pdk_action_lock.png new file mode 100755 index 0000000..093cf20 Binary files /dev/null and b/res/drawable-xhdpi/ic_pdk_action_lock.png differ diff --git a/res/drawable-xhdpi/ic_pdk_action_unlock.png b/res/drawable-xhdpi/ic_pdk_action_unlock.png new file mode 100755 index 0000000..5d14385 Binary files /dev/null and b/res/drawable-xhdpi/ic_pdk_action_unlock.png differ diff --git a/res/drawable-xxhdpi/ic_ambient_light.png b/res/drawable-xxhdpi/ic_ambient_light.png new file mode 100755 index 0000000..84d9cbb Binary files /dev/null and b/res/drawable-xxhdpi/ic_ambient_light.png differ diff --git a/res/drawable-xxhdpi/ic_pdk_accelerometer.png b/res/drawable-xxhdpi/ic_pdk_accelerometer.png new file mode 100755 index 0000000..d9970ce Binary files /dev/null and b/res/drawable-xxhdpi/ic_pdk_accelerometer.png differ diff --git a/res/drawable-xxhdpi/ic_pdk_action_lock.png b/res/drawable-xxhdpi/ic_pdk_action_lock.png new file mode 100755 index 0000000..620d614 Binary files /dev/null and b/res/drawable-xxhdpi/ic_pdk_action_lock.png differ diff --git a/res/drawable-xxhdpi/ic_pdk_action_unlock.png b/res/drawable-xxhdpi/ic_pdk_action_unlock.png new file mode 100755 index 0000000..0ffdc60 Binary files /dev/null and b/res/drawable-xxhdpi/ic_pdk_action_unlock.png differ diff --git a/res/drawable-xxxhdpi/ic_ambient_light.png b/res/drawable-xxxhdpi/ic_ambient_light.png new file mode 100755 index 0000000..d5c3269 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_ambient_light.png differ diff --git a/res/drawable-xxxhdpi/ic_pdk_accelerometer.png b/res/drawable-xxxhdpi/ic_pdk_accelerometer.png new file mode 100755 index 0000000..855cdd6 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_pdk_accelerometer.png differ diff --git a/res/drawable-xxxhdpi/ic_pdk_action_lock.png b/res/drawable-xxxhdpi/ic_pdk_action_lock.png new file mode 100755 index 0000000..ae3f973 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_pdk_action_lock.png differ diff --git a/res/drawable-xxxhdpi/ic_pdk_action_unlock.png b/res/drawable-xxxhdpi/ic_pdk_action_unlock.png new file mode 100755 index 0000000..bc597c2 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_pdk_action_unlock.png differ diff --git a/res/layout/card_generator_sensors_accelerometer.xml b/res/layout/card_generator_sensors_accelerometer.xml new file mode 100755 index 0000000..3f80ec3 --- /dev/null +++ b/res/layout/card_generator_sensors_accelerometer.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/card_generator_sensors_ambient_light.xml b/res/layout/card_generator_sensors_ambient_light.xml new file mode 100755 index 0000000..62dd685 --- /dev/null +++ b/res/layout/card_generator_sensors_ambient_light.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/activity_data_stream.xml b/res/menu/activity_data_stream.xml new file mode 100755 index 0000000..d6a6bbf --- /dev/null +++ b/res/menu/activity_data_stream.xml @@ -0,0 +1,8 @@ + + + + diff --git a/res/values/databases.xml b/res/values/databases.xml index 9e41810..0b74d73 100755 --- a/res/values/databases.xml +++ b/res/values/databases.xml @@ -27,8 +27,14 @@ CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, health TEXT, level INTERGER, plugged TEXT, present INTEGER, scale INTEGER, temperature INTEGER, voltage INTEGER, technology TEXT, status TEXT); - + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, application TEXT); ALTER TABLE history ADD duration REAL; + ALTER TABLE history ADD screen_active INTEGER; + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, light_level REAL, raw_timestamp BIGINT, accuracy INTEGER); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, x REAL, y REAL, z REAL, raw_timestamp BIGINT, accuracy INTEGER); diff --git a/res/values/generators.xml b/res/values/generators.xml index b4037cd..0150e72 100755 --- a/res/values/generators.xml +++ b/res/values/generators.xml @@ -158,4 +158,23 @@ Most Used (Last 24 Hours) Recently Used %1$s: %2$.0fm - \ No newline at end of file + + + Ambient Light + No ambient light levels have been recorded yet. + #FFD600 + #80FFEB3B + #ffFFEB3B + + + Accelerometer + No accelerometer readings have been recorded yet. + #3F51B5 + #80F44336 + #ffF44336 + #804CAF50 + #ff4CAF50 + #802196F3 + #ff2196F3 + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 73b220f..3c77eb0 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6,6 +6,8 @@ Data Stream Continue + Toggle Sort Lock + %d generator %d generators diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java index 3e80bb0..c3a33f4 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java @@ -1,11 +1,16 @@ package com.audacious_software.passive_data_kit.activities; +import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuItem; import com.audacious_software.passive_data_kit.activities.generators.DataPointsAdapter; import com.audacious_software.passive_data_kit.generators.Generators; @@ -15,6 +20,7 @@ public class DataStreamActivity extends AppCompatActivity implements Generators.GeneratorUpdatedListener { private DataPointsAdapter mAdapter = null; + private Menu mMenu = null; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -70,4 +76,50 @@ public void run() { } }); } + + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + this.getMenuInflater().inflate(R.menu.activity_data_stream, menu); + + this.mMenu = menu; + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_pdk_toggle_sort_lock) { + this.toggleSortLock(); + + return true; + } + + return true; + } + + private void toggleSortLock() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + + boolean locked = prefs.getBoolean(DataPointsAdapter.SORT_BY_UPDATED, DataPointsAdapter.SORT_BY_UPDATED_DEFAULT); + + MenuItem lockedItem = this.mMenu.findItem(R.id.action_pdk_toggle_sort_lock); + + if (locked) { + lockedItem.setIcon(R.drawable.ic_pdk_action_unlock); + } else { + lockedItem.setIcon(R.drawable.ic_pdk_action_lock); + } + + SharedPreferences.Editor e = prefs.edit(); + e.putBoolean(DataPointsAdapter.SORT_BY_UPDATED, (locked == false)); + e.apply(); + + this.mAdapter.notifyDataSetChanged(); + } } diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java index b934071..38cffc3 100755 --- a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java +++ b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java @@ -1,6 +1,8 @@ package com.audacious_software.passive_data_kit.activities.generators; import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; @@ -18,6 +20,9 @@ import java.util.List; public class DataPointsAdapter extends RecyclerView.Adapter { + public static final String SORT_BY_UPDATED = "com.audacious_software.passive_data_kit.activities.generators.DataPointsAdapter"; + public static final boolean SORT_BY_UPDATED_DEFAULT = true; + private Context mContext = null; @Override @@ -85,46 +90,57 @@ public int getItemCount() { } private void sortGenerators(final Context context, List> generators) { - Collections.sort(generators, new Comparator>() { - @Override - public int compare(Class one, Class two) { - long oneUpdated = 0; - - try { - Method oneGenerated = one.getDeclaredMethod("latestPointGenerated", Context.class); - - oneUpdated = (long) oneGenerated.invoke(null, context); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + if (prefs.getBoolean(DataPointsAdapter.SORT_BY_UPDATED, DataPointsAdapter.SORT_BY_UPDATED_DEFAULT)) { + Collections.sort(generators, new Comparator>() { + @Override + public int compare(Class one, Class two) { + long oneUpdated = 0; + + try { + Method oneGenerated = one.getDeclaredMethod("latestPointGenerated", Context.class); + + oneUpdated = (long) oneGenerated.invoke(null, context); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + long twoUpdated = 0; + + try { + Method twoGenerated = two.getDeclaredMethod("latestPointGenerated", Context.class); + + twoUpdated = (long) twoGenerated.invoke(null, context); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + if (oneUpdated < twoUpdated) { + return 1; + } else if (oneUpdated > twoUpdated) { + return -1; + } + + return 0; } - - long twoUpdated = 0; - - try { - Method twoGenerated = two.getDeclaredMethod("latestPointGenerated", Context.class); - - twoUpdated = (long) twoGenerated.invoke(null, context); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); + }); + } else { + Collections.sort(generators, new Comparator>() { + @Override + public int compare(Class one, Class two) { + return one.getCanonicalName().compareTo(two.getCanonicalName()); } - - if (oneUpdated < twoUpdated) { - return 1; - } else if (oneUpdated > twoUpdated) { - return -1; - } - - return 0; - } - }); + }); + } } public int getItemViewType (int position) { diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Battery.java b/src/com/audacious_software/passive_data_kit/generators/device/Battery.java index f940c35..6fead94 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Battery.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Battery.java @@ -11,7 +11,6 @@ import android.os.BatteryManager; import android.os.Bundle; import android.support.v4.content.ContextCompat; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -342,13 +341,9 @@ public String getFormattedValue(float value, AxisBase axis) { if (level != lastLevel) { values.add(0, new Entry(when, level)); lastLevel = level; - - Log.e("SLEEP-SIGHT", "VALUE: " + level + " -- " + (when - start)); } } - Log.e("SLEEP-SIGHT", "BATT VALUES COUNT 2: " + values.size()); - LineDataSet set = new LineDataSet(values, "Battery"); set.setAxisDependency(YAxis.AxisDependency.LEFT); set.setLineWidth(2.0f); diff --git a/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java b/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java index 4eb5410..90f4620 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java @@ -13,9 +13,11 @@ import android.os.Bundle; import android.provider.Settings; import android.util.Log; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -46,12 +48,13 @@ public class ForegroundApplication extends Generator{ private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.ForegroundApplication.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static int DATABASE_VERSION = 2; + private static int DATABASE_VERSION = 3; private static final String TABLE_HISTORY = "history"; private static final String HISTORY_OBSERVED = "observed"; private static final String HISTORY_APPLICATION = "application"; private static final String HISTORY_DURATION = "duration"; + private static final String HISTORY_SCREEN_ACTIVE = "screen_active"; private static ForegroundApplication sInstance = null; @@ -94,14 +97,26 @@ private void startGenerator() { this.mAppChecker.other(new AppChecker.Listener() { @Override public void onForeground(String process) { - Log.e("PDK", "PROCESS: " + process); - long now = System.currentTimeMillis(); + WindowManager window = (WindowManager) me.mContext.getSystemService(Context.WINDOW_SERVICE); + Display display = window.getDefaultDisplay(); + + boolean screenActive = true; + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (display.getState() != Display.STATE_ON) { + screenActive = false; + } + } + + Log.e("PDK", "PROCESS: " + process + " -- " + screenActive); + ContentValues values = new ContentValues(); values.put(ForegroundApplication.HISTORY_OBSERVED, now); values.put(ForegroundApplication.HISTORY_APPLICATION, process); values.put(ForegroundApplication.HISTORY_DURATION, me.mSampleInterval); + values.put(ForegroundApplication.HISTORY_SCREEN_ACTIVE, screenActive); me.mDatabase.insert(ForegroundApplication.TABLE_HISTORY, null, values); @@ -130,6 +145,8 @@ public void onForeground(String process) { this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_foreground_applications_create_history_table)); case 1: this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_foreground_applications_history_table_add_duration)); + case 2: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_foreground_applications_history_table_add_screen_active)); } this.setDatabaseVersion(this.mDatabase, ForegroundApplication.DATABASE_VERSION); @@ -200,8 +217,8 @@ public static void bindViewHolder(DataPointViewHolder holder) { ArrayList latest = new ArrayList<>(); - String where = ForegroundApplication.HISTORY_OBSERVED + " > ?"; - String[] args = { "" + yesterday }; + String where = ForegroundApplication.HISTORY_OBSERVED + " > ? AND " + ForegroundApplication.HISTORY_SCREEN_ACTIVE + " = ?"; + String[] args = { "" + yesterday, "1" }; c = generator.mDatabase.query(ForegroundApplication.TABLE_HISTORY, null, where, args, null, null, ForegroundApplication.HISTORY_OBSERVED); @@ -298,16 +315,19 @@ public int compare(HashMap mapOne, HashMap mapTw for (String key : appDef.keySet()) { double duration = appDef.get(key); + double minutes = duration / (1000 * 60); + try { String name = packageManager.getApplicationLabel(packageManager.getApplicationInfo(key, PackageManager.GET_META_DATA)).toString(); - double minutes = duration / (1000 * 60); - appName.setText(context.getString(R.string.generator_foreground_application_app_name_duration, name, minutes)); Drawable icon = packageManager.getApplicationIcon(key); appIcon.setImageDrawable(icon); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); + + appName.setText(context.getString(R.string.generator_foreground_application_app_name_duration, key, minutes)); + appIcon.setImageDrawable(null); } double remainder = largestUsage - duration; diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java index 3828e56..93e805d 100755 --- a/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java @@ -1,8 +1,658 @@ package com.audacious_software.passive_data_kit.generators.sensors; +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +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 com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +import java.io.File; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + /** * Created by cjkarr on 4/17/2017. */ -public class Accelerometer { +public class Accelerometer extends SensorGenerator implements SensorEventListener { + private static final String GENERATOR_IDENTIFIER = "pdk-sensor-accelerometer"; + + private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.Accelerometer.ENABLED"; + private static final boolean ENABLED_DEFAULT = true; + + private static final String DATABASE_PATH = "pdk-sensor-accelerometer.sqlite"; + private static final int DATABASE_VERSION = 1; + + public static final String TABLE_HISTORY = "history"; + + public static final String HISTORY_OBSERVED = "observed"; + private static final String HISTORY_RAW_TIMESTAMP = "raw_timestamp"; + private static final String HISTORY_ACCURACY = "accuracy"; + public static final String HISTORY_X = "x"; + public static final String HISTORY_Y = "y"; + public static final String HISTORY_Z = "z"; + + private static Accelerometer sInstance = null; + private static Handler sHandler = null; + + private static boolean sIsDrawing = false; + private static long sLastDrawStart = 0; + + private SQLiteDatabase mDatabase = null; + + private Sensor mSensor = null; + + private static int NUM_BUFFERS = 3; + private static int BUFFER_SIZE = 1024; + + private long mLastCleanup = 0; + private long mCleanupInterval = 15 * 60 * 1000; + + private int mActiveBuffersIndex = 0; + private int mCurrentBufferIndex = 0; + + private float[][] mXValueBuffers = null; + private float[][] mYValueBuffers = null; + private float[][] mZValueBuffers = null; + private int[][] mAccuracyBuffers = null; + private long[][] mRawTimestampBuffers = null; + private long[][] mTimestampBuffers = null; + + long mBaseTimestamp = 0; + + private long mLatestTimestamp = 0; + + public static Accelerometer getInstance(Context context) { + if (Accelerometer.sInstance == null) { + Accelerometer.sInstance = new Accelerometer(context.getApplicationContext()); + } + + return Accelerometer.sInstance; + } + + public Accelerometer(Context context) { + super(context); + } + + public static void start(final Context context) { + Accelerometer.getInstance(context).startGenerator(); + } + + private void startGenerator() { + final SensorManager sensors = (SensorManager) this.mContext.getSystemService(Context.SENSOR_SERVICE); + + final Accelerometer me = this; + + Generators.getInstance(this.mContext).registerCustomViewClass(Accelerometer.GENERATOR_IDENTIFIER, Accelerometer.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, Accelerometer.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_accelerometer_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, Accelerometer.DATABASE_VERSION); + + if (Accelerometer.isEnabled(this.mContext)) { + this.mSensor = sensors.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + + Runnable r = new Runnable() + { + public void run() + { + Looper.prepare(); + + me.mXValueBuffers = new float[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mYValueBuffers = new float[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mZValueBuffers = new float[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mAccuracyBuffers = new int[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mRawTimestampBuffers = new long[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + me.mTimestampBuffers = new long[Accelerometer.NUM_BUFFERS][Accelerometer.BUFFER_SIZE]; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; + + Accelerometer.sHandler = new Handler(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, 0, Accelerometer.sHandler); + else + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, Accelerometer.sHandler); + + Looper.loop(); + } + }; + + Thread t = new Thread(r, "accelerometer"); + t.start(); + } else { + if (this.mSensor != null) { + sensors.unregisterListener(this, this.mSensor); + + if (Accelerometer.sHandler != null) { + Looper loop = Accelerometer.sHandler.getLooper(); + loop.quit(); + + Accelerometer.sHandler = null; + } + + me.mXValueBuffers = null; + me.mYValueBuffers = null; + me.mZValueBuffers = null; + me.mAccuracyBuffers = null; + me.mRawTimestampBuffers = null; + me.mTimestampBuffers = null; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; + + this.mSensor = null; + } + } + } + + public static boolean isEnabled(Context context) { + SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); + + return prefs.getBoolean(Accelerometer.ENABLED, Accelerometer.ENABLED_DEFAULT); + } + + public static boolean isRunning(Context context) { + if (Accelerometer.sInstance == null) { + return false; + } + + return Accelerometer.sInstance.mSensor != null; + } + + public static ArrayList diagnostics(Context context) { + return new ArrayList<>(); + } + + public static void bindViewHolder(final DataPointViewHolder holder) { + if (Accelerometer.sIsDrawing) { + Log.e("PDK", "IS DRAWING"); + return; + } + + final long drawStart = System.currentTimeMillis(); + + if (drawStart - Accelerometer.sLastDrawStart < (30 * 1000)) { + Log.e("PDK", "TOO SOON"); + return; + } + + Accelerometer.sLastDrawStart = drawStart; + + Accelerometer.sIsDrawing = true; + + Log.e("PDK", "HOLDER " + holder.hashCode()); + + final Context context = holder.itemView.getContext(); + final View itemView = holder.itemView; + + final Accelerometer generator = Accelerometer.getInstance(context); + + final long now = System.currentTimeMillis() / (1000 * 60 * 5); + final long start = now - (24 * 12); // * 60); + + Log.e("PDK", "START QUERY: " + (System.currentTimeMillis() - drawStart)); + + Cursor c = generator.mDatabase.query(Accelerometer.TABLE_HISTORY, null, null, null, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); + + Log.e("PDK", "END QUERY: " + (System.currentTimeMillis() - drawStart)); + + View cardContent = itemView.findViewById(R.id.card_content); + View cardEmpty = itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) itemView.findViewById(R.id.generator_data_point_date); + + Log.e("PDK", "ACCEL PREP: " + (System.currentTimeMillis() - drawStart) + " -- COUNT: " + c.getCount()); + + if (c.moveToNext() && (context instanceof Activity)) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); + + long timestamp = c.getLong(c.getColumnIndex(Accelerometer.HISTORY_OBSERVED)) / (1000 * 1000 * 1000); + + dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + + Runnable r = new Runnable() { + @Override + public void run() { + Log.e("PDK", "THREAD START: " + (System.currentTimeMillis() - drawStart)); + + final ArrayList xLowValues = new ArrayList<>(); + final ArrayList xHighValues = new ArrayList<>(); + + final ArrayList yLowValues = new ArrayList<>(); + final ArrayList yHighValues = new ArrayList<>(); + + final ArrayList zLowValues = new ArrayList<>(); + final ArrayList zHighValues = new ArrayList<>(); + + final String where = Accelerometer.HISTORY_OBSERVED + " >= ? AND _id % 1024 = 0"; + final String[] args = { "" + start }; + + Cursor c = generator.mDatabase.query(Accelerometer.TABLE_HISTORY, null, where, args, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); + + long lastTimestamp = -1; + + float maxValue = 0; + float minValue = 0; + + float lowX = -1; + float highX = -1; + + float lowY = -1; + float highY = -1; + + float lowZ = -1; + float highZ = -1; + + int whenIndex = c.getColumnIndex(Accelerometer.HISTORY_OBSERVED); + int xIndex = c.getColumnIndex(Accelerometer.HISTORY_X); + int yIndex = c.getColumnIndex(Accelerometer.HISTORY_Y); + int zIndex = c.getColumnIndex(Accelerometer.HISTORY_Z); + + Log.e("PDK", "COUNT: " + c.getCount()); + Log.e("PDK", "ACCEL START BUILD: " + (System.currentTimeMillis() - drawStart)); + + while (c.moveToNext()) { + long when = c.getLong(whenIndex); + + when = when / (1000 * 1000); + when = when / (1000 * 6 * 50); + + float x = c.getFloat(xIndex); + float y = c.getFloat(yIndex); + float z = c.getFloat(zIndex); + + if (lastTimestamp != when) { + if (lastTimestamp != -1) { + xLowValues.add(0, new Entry(lastTimestamp, lowX)); + xHighValues.add(0, new Entry(lastTimestamp, highX)); + + yLowValues.add(0, new Entry(lastTimestamp, lowY)); + yHighValues.add(0, new Entry(lastTimestamp, highY)); + + zLowValues.add(0, new Entry(lastTimestamp, lowZ)); + zHighValues.add(0, new Entry(lastTimestamp, highZ)); + } + + lastTimestamp = when; + + lowX = x; + highX = x; + + lowY = y; + highY = y; + + lowZ = z; + highZ = z; + } else { + if (x < lowX) { + lowX = x; + } + + if (x > highX) { + highX = x; + } + + if (y < lowY) { + lowY = y; + } + + if (y > highY) { + highY = y; + } + + if (z < lowZ) { + lowZ = z; + } + + if (z > highZ) { + highZ = z; + } + } + + if (x > maxValue) { + maxValue = x; + } + + if (x < minValue) { + minValue = x; + } + + if (y > maxValue) { + maxValue = y; + } + + if (y < minValue) { + minValue = y; + } + + if (z > maxValue) { + maxValue = z; + } + + if (z < minValue) { + minValue = z; + } + } + + Log.e("PDK", "ACCEL END BUILD BUILD: " + (System.currentTimeMillis() - drawStart)); + + Log.e("PDK", "DATA COUNT: " + xLowValues.size()); + + if (lastTimestamp != -1) { + xLowValues.add(0, new Entry(lastTimestamp, lowX)); + xHighValues.add(0, new Entry(lastTimestamp, highX)); + + yLowValues.add(0, new Entry(lastTimestamp, lowY)); + yHighValues.add(0, new Entry(lastTimestamp, highY)); + + zLowValues.add(0, new Entry(lastTimestamp, lowZ)); + zHighValues.add(0, new Entry(lastTimestamp, highZ)); + } + + c.close(); + + Activity activity = (Activity) context; + + final float finalMaxValue = maxValue; + final float finalMinValue = minValue; + + Log.e("PDK", "THREAD HANDOFF: " + (System.currentTimeMillis() - drawStart)); + + final List> data = new ArrayList<>(); + data.add(xLowValues); + data.add(xHighValues); + data.add(yLowValues); + data.add(yHighValues); + data.add(zLowValues); + data.add(zHighValues); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + Log.e("PDK", "UI START: " + (System.currentTimeMillis() - drawStart)); + + int[] colors = { + R.color.generator_accelerometer_x_low, + R.color.generator_accelerometer_x_high, + R.color.generator_accelerometer_y_low, + R.color.generator_accelerometer_y_high, + R.color.generator_accelerometer_z_low, + R.color.generator_accelerometer_z_high + }; + + LineData chartData = new LineData(); + + for (int i = 0; i < colors.length; i++) { + int color = colors[i]; + + ArrayList entries = data.get(i); + + LineDataSet set = new LineDataSet(entries, ""); + set.setAxisDependency(YAxis.AxisDependency.LEFT); + set.setLineWidth(1.0f); + set.setDrawCircles(false); + set.setFillAlpha(192); + set.setDrawFilled(false); + set.setDrawValues(true); + set.setColor(ContextCompat.getColor(context, color)); + set.setDrawCircleHole(false); + set.setDrawValues(false); + set.setMode(LineDataSet.Mode.LINEAR); + + chartData.addDataSet(set); + } + + Log.e("PDK", "ACCEL START GRAPH: " + (System.currentTimeMillis() - drawStart)); + + final LineChart chart = (LineChart) itemView.findViewById(R.id.accelerometer_chart); + + if (chart != null) { + chart.setViewPortOffsets(0, 0, 0, 0); + chart.setHighlightPerDragEnabled(false); + chart.setHighlightPerTapEnabled(false); + chart.setBackgroundColor(ContextCompat.getColor(context, android.R.color.black)); + chart.setPinchZoom(false); + + final DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(context); + + final XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM_INSIDE); + xAxis.setTextSize(10f); + xAxis.setDrawAxisLine(true); + xAxis.setDrawGridLines(true); + xAxis.setCenterAxisLabels(true); + xAxis.setDrawLabels(true); + xAxis.setTextColor(ContextCompat.getColor(context, android.R.color.white)); + xAxis.setGranularityEnabled(true); + xAxis.setGranularity(1); + xAxis.setAxisMinimum(start); + xAxis.setAxisMaximum(now); + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + Date date = new Date((long) value * 5 * 60 * 1000); + + return timeFormat.format(date); + } + }); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + leftAxis.setDrawGridLines(true); + leftAxis.setDrawAxisLine(true); + leftAxis.setGranularityEnabled(true); + leftAxis.setTextColor(ContextCompat.getColor(context, android.R.color.white)); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setEnabled(false); + + chart.getLegend().setEnabled(false); + chart.getDescription().setEnabled(false); + + chart.setVisibleYRange((float) Math.floor(finalMinValue) - 1, (float) Math.ceil(finalMaxValue) + 1, YAxis.AxisDependency.LEFT); + chart.setData(chartData); + } + + Log.e("PDK", "UI END: " + (System.currentTimeMillis() - drawStart)); + + Accelerometer.sIsDrawing = false; + } + }); + } + }; + + Thread t = new Thread(r, "render_accelerometer_graph"); + t.start(); + + c.close(); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + + c.close(); + + Log.e("PDK", "MAIN DONE: " + (System.currentTimeMillis() - drawStart)); + } + + public static View fetchView(ViewGroup parent) + { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_sensors_accelerometer, parent, false); + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static long latestPointGenerated(Context context) { + Accelerometer me = Accelerometer.getInstance(context); + + if (me.mLatestTimestamp == 0) { + Cursor c = me.mDatabase.query(Accelerometer.TABLE_HISTORY, null, null, null, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + me.mLatestTimestamp = c.getLong(c.getColumnIndex(Accelerometer.HISTORY_OBSERVED) / (1000 * 1000)); + } + + c.close(); + } + + return me.mLatestTimestamp; + } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(Accelerometer.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } + + @Override + public void onSensorChanged(SensorEvent sensorEvent) { + long rawTimestamp = sensorEvent.timestamp; + + if (this.mBaseTimestamp == 0) { + this.mBaseTimestamp = (System.currentTimeMillis() * (1000 * 1000)) - rawTimestamp; + } + + int accuracy = sensorEvent.accuracy; + long normalizedTimestamp = this.mBaseTimestamp + rawTimestamp; + + if (this.mCurrentBufferIndex >= Accelerometer.BUFFER_SIZE) { + this.saveBuffer(this.mActiveBuffersIndex, this.mCurrentBufferIndex); + + this.mCurrentBufferIndex = 0; + this.mActiveBuffersIndex += 1; + + if (this.mActiveBuffersIndex >= Accelerometer.NUM_BUFFERS) { + this.mActiveBuffersIndex = 0; + } + } + +// Log.e("PDK", "ACCEL[" + this.mCurrentBufferIndex + "/" + this.mActiveBuffersIndex + "] = " + sensorEvent.values[0] + " -- " + sensorEvent.values[1] + " -- " + sensorEvent.values[2]); + + this.mXValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[0]; + this.mYValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[1]; + this.mZValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[2]; + this.mAccuracyBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = accuracy; + this.mRawTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = rawTimestamp; + this.mTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = normalizedTimestamp; + + this.mCurrentBufferIndex += 1; + } + + private void saveBuffer(final int bufferIndex, final int bufferSize) { + final Accelerometer me = this; + + me.mLatestTimestamp = System.currentTimeMillis(); + + Runnable r = new Runnable() { + @Override + public void run() { + long now = System.currentTimeMillis(); + + try { + me.mDatabase.beginTransaction(); + + for (int i = 0; i < bufferSize; i++) { + ContentValues values = new ContentValues(); + + values.put(Accelerometer.HISTORY_X, me.mXValueBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_Y, me.mYValueBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_Z, me.mZValueBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex][i]); + values.put(Accelerometer.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex][i]); + + me.mDatabase.insert(Accelerometer.TABLE_HISTORY, null, values); + } + + me.mDatabase.setTransactionSuccessful(); + } finally { + me.mDatabase.endTransaction(); + } + + Bundle update = new Bundle(); + update.putLong(Accelerometer.HISTORY_OBSERVED, now); + + Bundle sensorReadings = new Bundle(); + + sensorReadings.putFloatArray(Accelerometer.HISTORY_X, me.mXValueBuffers[bufferIndex]); + sensorReadings.putFloatArray(Accelerometer.HISTORY_Y, me.mYValueBuffers[bufferIndex]); + sensorReadings.putFloatArray(Accelerometer.HISTORY_Z, me.mZValueBuffers[bufferIndex]); + sensorReadings.putLongArray(Accelerometer.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex]); + sensorReadings.putLongArray(Accelerometer.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex]); + sensorReadings.putIntArray(Accelerometer.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex]); + + update.putBundle(SensorGenerator.SENSOR_DATA, sensorReadings); + SensorGenerator.addSensorMetadata(update, me.mSensor); + + Generators.getInstance(me.mContext).notifyGeneratorUpdated(Accelerometer.GENERATOR_IDENTIFIER, update); + + if (now - me.mLastCleanup > me.mCleanupInterval) { + me.mLastCleanup = now; + + long start = (now - (2 * 24 * 60 * 60 * 1000)) * 1000 * 1000; + + String where = Accelerometer.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start }; + + me.mDatabase.delete(Accelerometer.TABLE_HISTORY, where, args); + } + } + }; + + Thread t = new Thread(r, "accelerometer-save-buffer"); + t.start(); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int newAccuracy) { + // Do nothing... + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java index 4dbedf8..cba3bc2 100755 --- a/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java @@ -1,5 +1,6 @@ package com.audacious_software.passive_data_kit.generators.sensors; +import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; @@ -13,7 +14,6 @@ import android.os.Handler; import android.os.Looper; import android.support.v4.content.ContextCompat; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -24,7 +24,6 @@ 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.passive_data_kit.generators.device.Battery; import com.audacious_software.pdk.passivedatakit.R; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.AxisBase; @@ -45,19 +44,21 @@ * Created by cjkarr on 4/17/2017. */ -public class AmbientLight extends Generator implements SensorEventListener { +public class AmbientLight extends SensorGenerator implements SensorEventListener { private static final String GENERATOR_IDENTIFIER = "pdk-sensor-light"; private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.AmbientLight.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static final String DATABASE_PATH = "pdk-sensor-light.sqlite"; + private static final String DATABASE_PATH = "pdk-sensor-ambient-light.sqlite"; private static final int DATABASE_VERSION = 1; public static final String TABLE_HISTORY = "history"; public static final String HISTORY_OBSERVED = "observed"; public static final String HISTORY_LEVEL = "light_level"; + private static final String HISTORY_RAW_TIMESTAMP = "raw_timestamp"; + private static final String HISTORY_ACCURACY = "accuracy"; private static AmbientLight sInstance = null; private static Handler sHandler = null; @@ -66,6 +67,23 @@ public class AmbientLight extends Generator implements SensorEventListener { private Sensor mSensor = null; + private static int NUM_BUFFERS = 3; + private static int BUFFER_SIZE = 32; + + private long mLastCleanup = 0; + private long mCleanupInterval = 15 * 60 * 1000; + + private int mActiveBuffersIndex = 0; + private int mCurrentBufferIndex = 0; + + private float[][] mValueBuffers = null; + private int[][] mAccuracyBuffers = null; + private long[][] mRawTimestampBuffers = null; + private long[][] mTimestampBuffers = null; + + long mBaseTimestamp = 0; + private long mLatestTimestamp = 0; + public static AmbientLight getInstance(Context context) { if (AmbientLight.sInstance == null) { AmbientLight.sInstance = new AmbientLight(context.getApplicationContext()); @@ -87,6 +105,23 @@ private void startGenerator() { final AmbientLight me = this; + Generators.getInstance(this.mContext).registerCustomViewClass(AmbientLight.GENERATOR_IDENTIFIER, AmbientLight.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, AmbientLight.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_ambient_light_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, AmbientLight.DATABASE_VERSION); + if (AmbientLight.isEnabled(this.mContext)) { this.mSensor = sensors.getDefaultSensor(Sensor.TYPE_LIGHT); @@ -96,12 +131,20 @@ public void run() { Looper.prepare(); + me.mValueBuffers = new float[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + me.mAccuracyBuffers = new int[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + me.mRawTimestampBuffers = new long[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + me.mTimestampBuffers = new long[AmbientLight.NUM_BUFFERS][AmbientLight.BUFFER_SIZE]; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; + AmbientLight.sHandler = new Handler(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, 0, AmbientLight.sHandler); + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_FASTEST, 0, AmbientLight.sHandler); else - sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, AmbientLight.sHandler); + sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_FASTEST, AmbientLight.sHandler); Looper.loop(); } @@ -111,6 +154,8 @@ public void run() t.start(); } else { if (this.mSensor != null) { + sensors.unregisterListener(this, this.mSensor); + if (AmbientLight.sHandler != null) { Looper loop = AmbientLight.sHandler.getLooper(); loop.quit(); @@ -118,30 +163,17 @@ public void run() AmbientLight.sHandler = null; } - sensors.unregisterListener(this, this.mSensor); + me.mValueBuffers = null; + me.mAccuracyBuffers = null; + me.mRawTimestampBuffers = null; + me.mTimestampBuffers = null; + + me.mActiveBuffersIndex = 0; + me.mCurrentBufferIndex = 0; this.mSensor = null; } } - - - Generators.getInstance(this.mContext).registerCustomViewClass(AmbientLight.GENERATOR_IDENTIFIER, Battery.class); - - File path = PassiveDataKit.getGeneratorsStorage(this.mContext); - - path = new File(path, AmbientLight.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_device_ambient_light_create_history_table)); - } - - this.setDatabaseVersion(this.mDatabase, AmbientLight.DATABASE_VERSION); } public static boolean isEnabled(Context context) { @@ -167,8 +199,8 @@ public static void bindViewHolder(DataPointViewHolder holder) { AmbientLight generator = AmbientLight.getInstance(context); - long now = System.currentTimeMillis(); - long start = now - (24 * 60 * 60 * 1000); + long now = System.currentTimeMillis() / (1000 * 60 * 5); + long start = now - (24 * 12); // * 60); String where = AmbientLight.HISTORY_OBSERVED + " >= ?"; String[] args = { "" + start }; @@ -183,13 +215,13 @@ public static void bindViewHolder(DataPointViewHolder holder) { cardContent.setVisibility(View.VISIBLE); cardEmpty.setVisibility(View.GONE); - long timestamp = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)) / 1000; + long timestamp = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)) / (1000 * 1000 * 1000); dateLabel.setText(Generator.formatTimestamp(context, timestamp)); c.moveToPrevious(); - final LineChart chart = (LineChart) holder.itemView.findViewById(R.id.battery_level_chart); + final LineChart chart = (LineChart) holder.itemView.findViewById(R.id.light_chart); chart.setViewPortOffsets(0,0,0,0); chart.setHighlightPerDragEnabled(false); chart.setHighlightPerTapEnabled(false); @@ -213,7 +245,7 @@ public static void bindViewHolder(DataPointViewHolder holder) { xAxis.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { - Date date = new Date((long) value); + Date date = new Date((long) value * 5 * 60 * 1000); return timeFormat.format(date); } @@ -224,8 +256,6 @@ public String getFormattedValue(float value, AxisBase axis) { leftAxis.setDrawGridLines(true); leftAxis.setDrawAxisLine(true); leftAxis.setGranularityEnabled(true); - leftAxis.setAxisMaximum(110); - leftAxis.setAxisMinimum(-10); leftAxis.setTextColor(ContextCompat.getColor(context, android.R.color.white)); YAxis rightAxis = chart.getAxisRight(); @@ -234,38 +264,88 @@ public String getFormattedValue(float value, AxisBase axis) { chart.getLegend().setEnabled(false); chart.getDescription().setEnabled(false); - ArrayList values = new ArrayList<>(); + ArrayList lowValues = new ArrayList<>(); + ArrayList highValues = new ArrayList<>(); - long lastLevel = -1; + long lastTimestamp = -1; + + float maxValue = 0; + float minValue = 0; + + float lowLevel = -1; + float highLevel = -1; while (c.moveToNext()) { long when = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)); - long level = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_LEVEL)); - if (level != lastLevel) { - values.add(0, new Entry(when, level)); - lastLevel = level; + when = when / (1000 * 1000); + when = when / (1000 * 6 * 50); + + float level = c.getFloat(c.getColumnIndex(AmbientLight.HISTORY_LEVEL)); + + if (lastTimestamp != when) { + if (lastTimestamp != -1) { + lowValues.add(0, new Entry(lastTimestamp, lowLevel)); + highValues.add(0, new Entry(lastTimestamp, highLevel)); + } + + lastTimestamp = when; + lowLevel = level; + highLevel = level; + } else { + if (level < lowLevel) { + lowLevel = level; + } + + if (level > highLevel) { + highLevel = level; + } + } + + if (level > maxValue) { + maxValue = level; + } - Log.e("SLEEP-SIGHT", "VALUE: " + level + " -- " + (when - start)); + if (level < minValue) { + minValue = level; } } - Log.e("SLEEP-SIGHT", "LIGHT VALUES COUNT 2: " + values.size()); + if (lastTimestamp != -1) { + lowValues.add(0, new Entry(lastTimestamp, lowLevel)); + highValues.add(0, new Entry(lastTimestamp, highLevel)); + } - LineDataSet set = new LineDataSet(values, "Light Level"); + LineDataSet set = new LineDataSet(lowValues, "Low Light Levels"); set.setAxisDependency(YAxis.AxisDependency.LEFT); - set.setLineWidth(2.0f); + set.setLineWidth(1.0f); set.setDrawCircles(false); set.setFillAlpha(192); set.setDrawFilled(false); set.setDrawValues(true); - set.setColor(ContextCompat.getColor(context, R.color.generator_battery_plot)); + set.setColor(ContextCompat.getColor(context, R.color.generator_ambient_light_low)); set.setDrawCircleHole(false); set.setDrawValues(false); set.setMode(LineDataSet.Mode.LINEAR); - chart.setVisibleYRange(0, 120, YAxis.AxisDependency.LEFT); - chart.setData(new LineData(set)); + LineData chartData = new LineData(set); + + set = new LineDataSet(highValues, "High Light Levels"); + set.setAxisDependency(YAxis.AxisDependency.LEFT); + set.setLineWidth(1.0f); + set.setDrawCircles(false); + set.setFillAlpha(192); + set.setDrawFilled(false); + set.setDrawValues(true); + set.setColor(ContextCompat.getColor(context, R.color.generator_ambient_light_high)); + set.setDrawCircleHole(false); + set.setDrawValues(false); + set.setMode(LineDataSet.Mode.LINEAR); + + chartData.addDataSet(set); + + chart.setVisibleYRange((float) Math.floor(minValue) - 1, (float) Math.ceil(maxValue) + 1, YAxis.AxisDependency.LEFT); + chart.setData(chartData); } else { cardContent.setVisibility(View.GONE); cardEmpty.setVisibility(View.VISIBLE); @@ -278,7 +358,7 @@ public String getFormattedValue(float value, AxisBase axis) { public static View fetchView(ViewGroup parent) { - return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_device_battery, parent, false); + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_sensors_ambient_light, parent, false); } @Override @@ -287,19 +367,19 @@ public List fetchPayloads() { } public static long latestPointGenerated(Context context) { - long timestamp = 0; - AmbientLight me = AmbientLight.getInstance(context); - Cursor c = me.mDatabase.query(AmbientLight.TABLE_HISTORY, null, null, null, null, null, AmbientLight.HISTORY_OBSERVED + " DESC"); + if (me.mLatestTimestamp == 0) { + Cursor c = me.mDatabase.query(AmbientLight.TABLE_HISTORY, null, null, null, null, null, Accelerometer.HISTORY_OBSERVED + " DESC"); - if (c.moveToNext()) { - timestamp = c.getLong(c.getColumnIndex(AmbientLight.HISTORY_OBSERVED)); - } + if (c.moveToNext()) { + me.mLatestTimestamp = c.getLong(c.getColumnIndex(Accelerometer.HISTORY_OBSERVED) / (1000 * 1000)); + } - c.close(); + c.close(); + } - return timestamp; + return me.mLatestTimestamp; } public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { @@ -308,11 +388,98 @@ public Cursor queryHistory(String[] cols, String where, String[] args, String or @Override public void onSensorChanged(SensorEvent sensorEvent) { + long rawTimestamp = sensorEvent.timestamp; + + if (this.mBaseTimestamp == 0) { + this.mBaseTimestamp = (System.currentTimeMillis() * (1000 * 1000)) - rawTimestamp; + } + + int accuracy = sensorEvent.accuracy; + long normalizedTimestamp = this.mBaseTimestamp + rawTimestamp; + float value = sensorEvent.values[0]; + + if (this.mCurrentBufferIndex >= AmbientLight.BUFFER_SIZE) { + this.saveBuffer(this.mActiveBuffersIndex, this.mCurrentBufferIndex); + + this.mCurrentBufferIndex = 0; + this.mActiveBuffersIndex += 1; + + if (this.mActiveBuffersIndex >= AmbientLight.NUM_BUFFERS) { + this.mActiveBuffersIndex = 0; + } + } + this.mValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = value; + this.mAccuracyBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = accuracy; + this.mRawTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = rawTimestamp; + this.mTimestampBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = normalizedTimestamp; + + this.mCurrentBufferIndex += 1; + } + + private void saveBuffer(final int bufferIndex, final int bufferSize) { + final AmbientLight me = this; + + me.mLatestTimestamp = System.currentTimeMillis(); + + Runnable r = new Runnable() { + @Override + public void run() { + long now = System.currentTimeMillis(); + + try { + me.mDatabase.beginTransaction(); + + for (int i = 0; i < bufferSize; i++) { + ContentValues values = new ContentValues(); + + values.put(AmbientLight.HISTORY_LEVEL, me.mValueBuffers[bufferIndex][i]); + values.put(AmbientLight.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex][i]); + values.put(AmbientLight.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex][i]); + values.put(AmbientLight.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex][i]); + + me.mDatabase.insert(AmbientLight.TABLE_HISTORY, null, values); + } + + me.mDatabase.setTransactionSuccessful(); + } finally { + me.mDatabase.endTransaction(); + } + + Bundle update = new Bundle(); + update.putLong(AmbientLight.HISTORY_OBSERVED, now); + + Bundle sensorReadings = new Bundle(); + + sensorReadings.putFloatArray(AmbientLight.HISTORY_LEVEL, me.mValueBuffers[bufferIndex]); + sensorReadings.putLongArray(AmbientLight.HISTORY_RAW_TIMESTAMP, me.mRawTimestampBuffers[bufferIndex]); + sensorReadings.putLongArray(AmbientLight.HISTORY_OBSERVED, me.mTimestampBuffers[bufferIndex]); + sensorReadings.putIntArray(AmbientLight.HISTORY_ACCURACY, me.mAccuracyBuffers[bufferIndex]); + + update.putBundle(SensorGenerator.SENSOR_DATA, sensorReadings); + SensorGenerator.addSensorMetadata(update, me.mSensor); + + Generators.getInstance(me.mContext).notifyGeneratorUpdated(AmbientLight.GENERATOR_IDENTIFIER, update); + + if (now - me.mLastCleanup > me.mCleanupInterval) { + me.mLastCleanup = now; + + long start = (now - (2 * 24 * 60 * 60 * 1000)) * 1000 * 1000; + + String where = AmbientLight.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start }; + + me.mDatabase.delete(AmbientLight.TABLE_HISTORY, where, args); + } + } + }; + + Thread t = new Thread(r, "ambient-light-save-buffer"); + t.start(); } @Override public void onAccuracyChanged(Sensor sensor, int newAccuracy) { - + // Do nothing... } } diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/SensorGenerator.java b/src/com/audacious_software/passive_data_kit/generators/sensors/SensorGenerator.java new file mode 100755 index 0000000..947858b --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/SensorGenerator.java @@ -0,0 +1,74 @@ +package com.audacious_software.passive_data_kit.generators.sensors; + +import android.content.Context; +import android.hardware.Sensor; +import android.os.Build; +import android.os.Bundle; + +import com.audacious_software.passive_data_kit.generators.Generator; + +/** + * Created by cjkarr on 5/11/2017. + */ + +public abstract class SensorGenerator extends Generator { + public static final String SENSOR_DATA = "sensor_data"; + + private static final String SENSOR_REPORTING_MODE = "reporting_mode"; + private static final String SENSOR_NAME = "name"; + private static final String SENSOR_VENDOR = "vendor"; + private static final String SENSOR_MAX_RANGE = "max_range"; + private static final String SENSOR_POWER_USAGE = "power_usage"; + private static final String SENSOR_RESOLUTION = "resolution"; + private static final String SENSOR_MIN_DELAY = "min_delay"; + private static final String SENSOR_VERSION = "version"; + private static final String SENSOR_TYPE = "type"; + private static final String SENSOR_MAX_DELAY = "max_delay"; + private static final String SENSOR_IS_WAKEUP = "is_wakeup"; + + private static final String SENSOR_REPORTING_MODE_CONTINUOUS = "continuous"; + private static final String SENSOR_REPORTING_MODE_ON_CHANGE = "on_change"; + private static final String SENSOR_REPORTING_MODE_ONE_SHOT = "one_shot"; + private static final String SENSOR_REPORTING_MODE_SPECIAL_TRIGGER = "special_trigger"; + + public SensorGenerator(Context context) { + super(context); + } + + public static void addSensorMetadata(Bundle update, Sensor sensor) { + update.putString(SensorGenerator.SENSOR_NAME, sensor.getName()); + update.putString(SensorGenerator.SENSOR_VENDOR, sensor.getVendor()); + + update.putFloat(SensorGenerator.SENSOR_MAX_RANGE, sensor.getMaximumRange()); + update.putFloat(SensorGenerator.SENSOR_POWER_USAGE, sensor.getPower()); + update.putFloat(SensorGenerator.SENSOR_RESOLUTION, sensor.getResolution()); + + update.putInt(SensorGenerator.SENSOR_MIN_DELAY, sensor.getMinDelay()); + update.putInt(SensorGenerator.SENSOR_VERSION, sensor.getVersion()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + update.putString(SensorGenerator.SENSOR_TYPE, sensor.getStringType()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + update.putInt(SensorGenerator.SENSOR_MAX_DELAY, sensor.getMaxDelay()); + + switch(sensor.getReportingMode()) { + case Sensor.REPORTING_MODE_CONTINUOUS: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_CONTINUOUS); + break; + case Sensor.REPORTING_MODE_ON_CHANGE: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_ON_CHANGE); + break; + case Sensor.REPORTING_MODE_ONE_SHOT: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_ONE_SHOT); + break; + case Sensor.REPORTING_MODE_SPECIAL_TRIGGER: + update.putString(SensorGenerator.SENSOR_REPORTING_MODE, SensorGenerator.SENSOR_REPORTING_MODE_SPECIAL_TRIGGER); + break; + } + + update.putBoolean(SensorGenerator.SENSOR_IS_WAKEUP, sensor.isWakeUpSensor()); + } + } + } +}