From e79f4523c332f1624fc3579294c5fcf30ecb27db Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 30 May 2015 17:28:03 +0200 Subject: [PATCH] Add SQLite database support for activity data This is now used for morpheuz data and the SleepMonitorActivity --- .../gadgetbridge/AbstractDeviceSupport.java | 1 - .../freeyourgadget/gadgetbridge/GB.java | 2 - .../gadgetbridge/GBActivitySample.java | 38 +++++ .../gadgetbridge/GBApplication.java | 8 + .../gadgetbridge/SleepMonitorActivity.java | 159 +++++++++++------- .../database/ActivityDatabaseHandler.java | 107 ++++++++++++ .../gadgetbridge/pebble/MorpheuzSupport.java | 15 +- .../GBDeviceCommandSleepMonitorResult.java | 1 - 8 files changed, 259 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBActivitySample.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractDeviceSupport.java index bd5e306f..f811f724 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/AbstractDeviceSupport.java @@ -132,7 +132,6 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { sleepMontiorIntent.putExtra("smartalarm_to", sleepMonitorResult.smartalarm_to); sleepMontiorIntent.putExtra("recording_base_timestamp", sleepMonitorResult.recording_base_timestamp); sleepMontiorIntent.putExtra("alarm_gone_off", sleepMonitorResult.alarm_gone_off); - sleepMontiorIntent.putExtra("points", sleepMonitorResult.points); LocalBroadcastManager.getInstance(context).sendBroadcast(sleepMontiorIntent); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GB.java index 0d9874dd..bb7659f4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GB.java @@ -7,9 +7,7 @@ import android.bluetooth.BluetoothAdapter; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import org.slf4j.Logger; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBActivitySample.java new file mode 100644 index 00000000..5348b6de --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBActivitySample.java @@ -0,0 +1,38 @@ +package nodomain.freeyourgadget.gadgetbridge; + +public class GBActivitySample { + private final int timestamp; + private final byte provider; + private final short intensity; + private final byte steps; + private final byte type; + + public GBActivitySample(int timestamp, byte provider, short intensity, byte steps, byte type) { + this.timestamp = timestamp; + this.provider = provider; + this.intensity = intensity; + this.steps = steps; + this.type = type; + } + + public int getTimestamp() { + return timestamp; + } + + public byte getProvider() { + return provider; + } + + public short getIntensity() { + return intensity; + } + + public byte getSteps() { + return steps; + } + + public byte getType() { + return type; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 72242f4f..9874de77 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -10,11 +10,15 @@ import org.slf4j.LoggerFactory; import java.io.File; +import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; + public class GBApplication extends Application { private static GBApplication context; + private static ActivityDatabaseHandler mActivityDatabaseHandler; public GBApplication() { context = this; + mActivityDatabaseHandler = new ActivityDatabaseHandler(context); } @Override @@ -57,4 +61,8 @@ public class GBApplication extends Application { public static Context getContext() { return context; } + + public static ActivityDatabaseHandler getActivityDatabaseHandler() { + return mActivityDatabaseHandler; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepMonitorActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepMonitorActivity.java index c85904a6..60e0231d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepMonitorActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepMonitorActivity.java @@ -21,11 +21,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; -public class SleepMonitorActivity extends Activity { +public class SleepMonitorActivity extends Activity implements SurfaceHolder.Callback { public static final String ACTION_REFRESH = "nodomain.freeyourgadget.gadgetbride.sleepmonitor.action.refresh"; private static final Logger LOG = LoggerFactory.getLogger(SleepMonitorActivity.class); @@ -33,6 +34,11 @@ public class SleepMonitorActivity extends Activity { private SurfaceView surfaceView; private TextView textView; + private int mSmartAlarmFrom = -1; + private int mSmartAlarmTo = -1; + private int mTimestampFrom = -1; + private int mSmartAlarmGoneOff = -1; + private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -40,69 +46,86 @@ public class SleepMonitorActivity extends Activity { if (action.equals(ControlCenter.ACTION_QUIT)) { finish(); } else if (action.equals(ACTION_REFRESH)) { - int smartalarm_from = intent.getIntExtra("smartalarm_from", -1); - int smartalarm_to = intent.getIntExtra("smartalarm_to", -1); - int recording_base_timestamp = intent.getIntExtra("recording_base_timestamp", -1); - int alarm_gone_off = intent.getIntExtra("alarm_gone_off", -1); - short[] points = intent.getShortArrayExtra("points"); - - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis((long) recording_base_timestamp * 1000L); - Date date = cal.getTime(); - String dateString = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(date); - cal.setTimeInMillis((long) (recording_base_timestamp + 600 * (points.length - 1)) * 1000L); - date = cal.getTime(); - String dateStringTo = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(date); - textView.setText(dateString + " to " + dateStringTo); - - SurfaceHolder surfaceHolder = surfaceView.getHolder(); - - if (surfaceHolder.getSurface().isValid() && points.length > 1) { - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - Canvas canvas = surfaceHolder.lockCanvas(); - paint.setColor(Color.WHITE); - paint.setStrokeWidth(2); - paint.setTextSize(20); - paint.setStyle(Paint.Style.FILL); - paint.setAntiAlias(true); - canvas.drawRGB(100, 100, 100); - int width = canvas.getWidth(); - int height = canvas.getHeight(); - - RectF r = new RectF(0.0f, 0.0f, 0.0f, height); - short last_movement = 5000; - for (int i = 0; i < points.length; i++) { - boolean annotate = false; - short movement = points[i]; - r.left = r.right; - r.right = (float) (i + 1) / (points.length) * width; - r.top = (1.0f - (float) movement / 5000.0f) * height; - if (movement > 1000) { - paint.setColor(Color.RED); - if (last_movement <= 1000) { - annotate = true; - } - } else if (movement > 120) { - paint.setColor(Color.YELLOW); - } else { - paint.setColor(Color.GREEN); - } - canvas.drawRect(r, paint); - if (annotate) { - cal.setTimeInMillis((long) (recording_base_timestamp + 600 * i) * 1000L); - date = cal.getTime(); - dateString = new SimpleDateFormat("HH:mm").format(date); - paint.setColor(Color.WHITE); - canvas.drawText(dateString, r.left - 20, r.top - 20, paint); - } - last_movement = movement; - } - surfaceHolder.unlockCanvasAndPost(canvas); - } + mSmartAlarmFrom = intent.getIntExtra("smartalarm_from", -1); + mSmartAlarmTo = intent.getIntExtra("smartalarm_to", -1); + mTimestampFrom = intent.getIntExtra("recording_base_timestamp", -1); + mSmartAlarmGoneOff = intent.getIntExtra("alarm_gone_off", -1); + refresh(); } } }; + private void refresh() { + if (mTimestampFrom == -1) { + Long ts = System.currentTimeMillis(); + mTimestampFrom = (int) ((ts / 1000) - (24 * 60 * 60) & 0xffffffff); // -24 hours + } + + ArrayList samples = GBApplication.getActivityDatabaseHandler().getGBActivitySamples(mTimestampFrom, -1, (byte) 1); + Calendar cal = Calendar.getInstance(); + Date date; + String dateStringFrom = ""; + String dateStringTo = ""; + + SurfaceHolder surfaceHolder = surfaceView.getHolder(); + LOG.info("number of samples:" + samples.size()); + if (surfaceHolder.getSurface().isValid() && samples.size() > 1) { + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Canvas canvas = surfaceHolder.lockCanvas(); + paint.setColor(Color.WHITE); + paint.setStrokeWidth(2); + paint.setTextSize(20); + paint.setStyle(Paint.Style.FILL); + paint.setAntiAlias(true); + canvas.drawRGB(100, 100, 100); + int width = canvas.getWidth(); + int height = canvas.getHeight(); + + RectF r = new RectF(0.0f, 0.0f, 0.0f, height); + short last_movement = 5000; + for (int i = 0; i < samples.size(); i++) { + GBActivitySample sample = samples.get(i); + + if (i == 0) { + cal.setTimeInMillis((long) sample.getTimestamp() * 1000L); + date = cal.getTime(); + dateStringFrom = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(date); + } else if (i == samples.size() - 1) { + cal.setTimeInMillis((long) sample.getTimestamp() * 1000L); + date = cal.getTime(); + dateStringTo = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(date); + } + boolean annotate = false; + short movement = sample.getIntensity(); + r.left = r.right; + r.right = (float) (i + 1) / (samples.size()) * width; + r.top = (1.0f - (float) movement / 5000.0f) * height; + if (movement > 1000) { + paint.setColor(Color.RED); + if (last_movement <= 1000) { + annotate = true; + } + } else if (movement > 120) { + paint.setColor(Color.YELLOW); + } else { + paint.setColor(Color.GREEN); + } + canvas.drawRect(r, paint); + if (annotate) { + cal.setTimeInMillis((long) (sample.getTimestamp()) * 1000L); + date = cal.getTime(); + String dateString = new SimpleDateFormat("HH:mm").format(date); + paint.setColor(Color.WHITE); + canvas.drawText(dateString, r.left - 20, r.top - 20, paint); + } + last_movement = movement; + } + textView.setText(dateStringFrom + " to " + dateStringTo); + + surfaceHolder.unlockCanvasAndPost(canvas); + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -118,6 +141,7 @@ public class SleepMonitorActivity extends Activity { filter.addAction(ACTION_REFRESH); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); + surfaceView.getHolder().addCallback(this); } @Override @@ -135,4 +159,19 @@ public class SleepMonitorActivity extends Activity { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); super.onDestroy(); } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + refresh(); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + refresh(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java new file mode 100644 index 00000000..e073cc1a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -0,0 +1,107 @@ +package nodomain.freeyourgadget.gadgetbridge.database; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import java.util.ArrayList; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.GBActivitySample; + +public class ActivityDatabaseHandler extends SQLiteOpenHelper { + + private static final int DATABASE_VERSION = 3; + + private static final String DATABASE_NAME = "ActivityDatabase"; + + private static final String TABLE_GBACTIVITYSAMPLES = "GBActivitySamples"; + + private static final String KEY_ID = "id"; + private static final String KEY_TIMESTAMP = "timestamp"; + private static final String KEY_PROVIDER = "provider"; + private static final String KEY_INTENSITY = "intensity"; + private static final String KEY_STEPS = "steps"; + private static final String KEY_TYPE = "type"; + + public ActivityDatabaseHandler(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + String CREATE_GBACTIVITYSAMPLES_TABLE = "CREATE TABLE " + TABLE_GBACTIVITYSAMPLES + "(" + + KEY_ID + " INTEGER PRIMARY KEY," + + KEY_TIMESTAMP + " INT," + + KEY_PROVIDER + " TINYINT," + + KEY_INTENSITY + " SMALLINT," + + KEY_STEPS + " TINYINT," + + KEY_TYPE + " TINYINT," + + " UNIQUE (" + KEY_TIMESTAMP + "," + KEY_PROVIDER + ") ON CONFLICT REPLACE)"; + db.execSQL(CREATE_GBACTIVITYSAMPLES_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + //FIXME: do not just recreate + db.execSQL("DROP TABLE IF EXISTS " + TABLE_GBACTIVITYSAMPLES); + onCreate(db); + } + + + public void addGBActivitySample(GBActivitySample GBActivitySample) { + SQLiteDatabase db = this.getWritableDatabase(); + + ContentValues values = new ContentValues(); + values.put(KEY_TIMESTAMP, GBActivitySample.getTimestamp()); + values.put(KEY_PROVIDER, GBActivitySample.getProvider()); + values.put(KEY_INTENSITY, GBActivitySample.getIntensity()); + values.put(KEY_STEPS, GBActivitySample.getSteps()); + values.put(KEY_TYPE, GBActivitySample.getType()); + + db.insert(TABLE_GBACTIVITYSAMPLES, null, values); + db.close(); + } + + public void addGBActivitySample(int timestamp, byte provider, short intensity, byte steps, byte type) { + SQLiteDatabase db = this.getWritableDatabase(); + + ContentValues values = new ContentValues(); + values.put(KEY_TIMESTAMP, timestamp); + values.put(KEY_PROVIDER, provider); + values.put(KEY_INTENSITY, intensity); + values.put(KEY_STEPS, steps); + values.put(KEY_TYPE, type); + + db.insert(TABLE_GBACTIVITYSAMPLES, null, values); + db.close(); + } + + public ArrayList getGBActivitySamples(int timestamp_from, int timestamp_to, byte provider) { + if (timestamp_to == -1) { + timestamp_to = 2147483647; // dont know what happens when I use more than max of a signed int + } + ArrayList GBActivitySampleList = new ArrayList(); + String selectQuery = "SELECT * FROM " + TABLE_GBACTIVITYSAMPLES + + " where (provider=" + provider + " and timestamp>=" + timestamp_from + " and timestamp<=" + timestamp_to + ")"; + + SQLiteDatabase db = this.getWritableDatabase(); + Cursor cursor = db.rawQuery(selectQuery, null); + + if (cursor.moveToFirst()) { + do { + GBActivitySample GBActivitySample = new GBActivitySample( + cursor.getInt(cursor.getColumnIndex(KEY_TIMESTAMP)), + (byte) cursor.getInt(cursor.getColumnIndex(KEY_PROVIDER)), + (short) cursor.getInt(cursor.getColumnIndex(KEY_INTENSITY)), + (byte) cursor.getInt(cursor.getColumnIndex(KEY_STEPS)), + (byte) cursor.getInt(cursor.getColumnIndex(KEY_TYPE))); + GBActivitySampleList.add(GBActivitySample); + } while (cursor.moveToNext()); + } + + return GBActivitySampleList; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/MorpheuzSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/MorpheuzSupport.java index 4056ff32..203ffba9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/MorpheuzSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/MorpheuzSupport.java @@ -11,6 +11,7 @@ import java.util.SimpleTimeZone; import java.util.TimeZone; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.protocol.GBDeviceCommand; import nodomain.freeyourgadget.gadgetbridge.protocol.GBDeviceCommandSendBytes; import nodomain.freeyourgadget.gadgetbridge.protocol.GBDeviceCommandSleepMonitorResult; @@ -37,8 +38,6 @@ public class MorpheuzSupport { private boolean sent_to_gadgetbridge = false; // data received from Morpheuz in native format - private short[] points = new short[54]; - private int points_last_valid = -1; private int smartalarm_from = -1; // time in minutes relative from 0:00 for smart alarm (earliest) private int smartalarm_to = -1;// time in minutes relative from 0:00 for smart alarm (latest) private int recording_base_timestamp = -1; // timestamp for the first "point", all folowing are +10 minutes offset each @@ -69,9 +68,12 @@ public class MorpheuzSupport { for (Pair pair : pairs) { int ctrl_message = 0; switch (pair.first) { + case KEY_TRANSMIT: case KEY_GONEOFF: - alarm_gone_off = (int) pair.second; - LOG.info("got gone off: " + alarm_gone_off / 60 + ":" + alarm_gone_off % 60); + if (pair.first == KEY_GONEOFF) { + alarm_gone_off = (int) pair.second; + LOG.info("got gone off: " + alarm_gone_off / 60 + ":" + alarm_gone_off % 60); + } /* super-ugly hack: if if did not notice GadgetBridge yet, do so and delay confirmation so Morpheuz * will resend gone off data. The second time, we acknowledge it. * @@ -81,8 +83,6 @@ public class MorpheuzSupport { ctrl_message = MorpheuzSupport.CTRL_VERSION_DONE | MorpheuzSupport.CTRL_GONEOFF_DONE | MorpheuzSupport.CTRL_TRANSMIT_DONE | MorpheuzSupport.CTRL_SET_LAST_SENT; } else { GBDeviceCommandSleepMonitorResult sleepMonitorResult = new GBDeviceCommandSleepMonitorResult(); - sleepMonitorResult.points = new short[points_last_valid + 1]; - System.arraycopy(points, 0, sleepMonitorResult.points, 0, points_last_valid + 1); sleepMonitorResult.smartalarm_from = smartalarm_from; sleepMonitorResult.smartalarm_to = smartalarm_to; sleepMonitorResult.alarm_gone_off = alarm_gone_off; @@ -100,8 +100,7 @@ public class MorpheuzSupport { short data = (short) ((int) pair.second & 0xffff); LOG.info("got point:" + index + " " + data); if (index >= 0 && index < 54) { - points[index] = data; - points_last_valid = index; + GBApplication.getActivityDatabaseHandler().addGBActivitySample(recording_base_timestamp + index * 600, (byte) 1, data, (byte) 0, (byte) 0); } ctrl_message = MorpheuzSupport.CTRL_VERSION_DONE | MorpheuzSupport.CTRL_SET_LAST_SENT | MorpheuzSupport.CTRL_DO_NEXT; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceCommandSleepMonitorResult.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceCommandSleepMonitorResult.java index 3937448a..90d6d2fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceCommandSleepMonitorResult.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/protocol/GBDeviceCommandSleepMonitorResult.java @@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.protocol; public class GBDeviceCommandSleepMonitorResult extends GBDeviceCommand { // FIXME: this is just the low-level data from Morpheuz, we need something generic - public short[] points; public int smartalarm_from = -1; // time in minutes relative from 0:00 for smart alarm (earliest) public int smartalarm_to = -1;// time in minutes relative from 0:00 for smart alarm (latest) public int recording_base_timestamp = -1; // timestamp for the first "point", all folowing are +10 minutes offset each