/*
* Copyright (C) 2012 Daryl Daly
*
* This file is part of Heart Observe
*
* Heart Observe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Heart Observe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.ddaly.android.heart;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.Cursor;
import android.content.Context;
import android.content.ContentValues;
import android.os.AsyncTask;
import android.os.Build;
import android.widget.SimpleCursorAdapter;
import android.widget.ListAdapter;
import android.widget.TextView;
import android.util.Log;
import android.text.format.DateUtils;
import java.util.ArrayList;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "DatabaseHelper";
private static final String DATABASE_NAME="heart.db";
private static final int SCHEMA_VERSION=4;
private static DatabaseHelper singleton=null;
private Context ctxt=null;
static final String TABLE="heart";
static final String ID="_id";
static final String DATE="date";
static final String SYSTOLIC="systolic";
static final String DIASTOLIC="diastolic";
static final String PULSE="pulse";
static final String NOTES="notes";
static final String LOCATION="location";
static final String SIDE="side";
private ArrayList<RecordChangedListener> recordChangedListeners = new ArrayList<RecordChangedListener>();
private Long requestSerialNo = 1L;
synchronized static DatabaseHelper getInstance(Context ctxt) {
if (singleton == null) {
singleton=new DatabaseHelper(ctxt.getApplicationContext());
}
return(singleton);
}
public DatabaseHelper(Context ctxt) {
super(ctxt, DATABASE_NAME, null, SCHEMA_VERSION);
this.ctxt=ctxt;
}
@Override
public void onCreate(SQLiteDatabase db) {
// location : True = upper_arm, False = forearm
// side : True = left, False = right
try {
db.beginTransaction();
db.execSQL("create table heart (_id integer primary key autoincrement, date datetime, systolic integer, notes varchar(50), diastolic integer, pulse integer, location boolean, side boolean);");
if (BuildConfig.DEBUG) {
loadTestData(db);
}
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
if (newVersion == 2) {
try {
db.beginTransaction();
db.execSQL("alter table heart add column diastolic integer;");
db.execSQL("alter table heart add column heart_rate integer;");
db.execSQL("alter table heart add column upper_arm boolean;");
db.execSQL("alter table heart add column left boolean;");
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
if (newVersion == 3) {
// not a good conversion -- okay for now because versions prior to 3 were never deployed
try {
db.beginTransaction();
db.execSQL("alter table heart rename to heart_v2;");
db.execSQL("create table heart (_id integer primary key autoincrement, date datetime, systolic integer, notes varchar(50), diastolic integer, heart_rate integer, location boolean, side boolean);");
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
if (newVersion == 4) {
// not a good conversion -- okay for now because versions prior to 4 were never deployed
try {
db.beginTransaction();
db.execSQL("drop table heart_v2;");
db.execSQL("alter table heart rename to heart_v2;");
db.execSQL("create table heart (_id integer primary key autoincrement, date datetime, systolic integer, notes varchar(50), diastolic integer, pulse integer, location boolean, side boolean);");
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
}
interface RecordChangedListener {
void recordChanged(Long id);
}
interface RecordListener{
void setRecord(ContentValues rec);
void setId(Long id);
}
interface ListAdapterListener {
void setListAdapter(ListAdapter adapter);
}
public void addRecordChangedListener(RecordChangedListener listener) {
recordChangedListeners.add(listener);
if (BuildConfig.DEBUG) {
Log.v (TAG, "addRecordChangedListener: size = " + recordChangedListeners.size());
}
}
public void removeRecordChangedListener(RecordChangedListener listener) {
recordChangedListeners.remove(listener);
if (BuildConfig.DEBUG) {
Log.v (TAG, "removeRecordChangedListener: size = " + recordChangedListeners.size());
}
}
void loadListAsync(ListAdapterListener listener) {
new LoadListTask(listener).execute();
}
private class LoadListTask extends AsyncTask<Void, Void, Void> {
private Cursor heartCursor = null;
private ListAdapterListener listener = null;
private String[] columns = {SYSTOLIC,DIASTOLIC,PULSE,DATE};
LoadListTask (ListAdapterListener listener) {
this.listener = listener;
}
@Override
protected Void doInBackground(Void... params) {
heartCursor = getReadableDatabase().query(TABLE,new String[] {ID,SYSTOLIC,DIASTOLIC,PULSE,DATE},null,null,null,null,"date desc");
// TODO -- should really be using columns value plus ID
// -- java seems make this harder then it should be!!!!
heartCursor.getCount(); // force query to execute
return(null);
}
@SuppressWarnings("deprecation")
@Override
public void onPostExecute(Void nothing) {
SimpleCursorAdapter adapter;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
adapter=new SimpleCursorAdapter(ctxt,
R.layout.row,
heartCursor,
columns,
new int[] {R.id.systolic,R.id.diastolic,R.id.pulse,R.id.date}
, 0) {
@Override
public void setViewText(TextView v, String text) {
super.setViewText(v, convText(v, text));
}
};
} else {
adapter=new SimpleCursorAdapter(ctxt,
R.layout.row,
heartCursor,
columns,
new int[] {R.id.systolic,R.id.diastolic,R.id.pulse,R.id.date}
) {
@Override
public void setViewText(TextView v, String text) {
super.setViewText(v, convText(v, text));
}
};
}
listener.setListAdapter(adapter);
}
private String convText(TextView v, String text) {
switch (v.getId()) {
case R.id.date:
String formatedText = text;
formatedText = DateUtils.formatDateTime(ctxt, Long.parseLong(text), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
return formatedText;
}
return text;
}
}
void getRecordAsync(Long id, RecordListener listener) {
new GetRecordTask(listener).execute(id);
}
private class GetRecordTask extends AsyncTask<Long, Void, ContentValues> {
private RecordListener listener = null;
GetRecordTask(RecordListener listener) {
this.listener=listener;
}
@Override
protected ContentValues doInBackground (Long... params) {
ContentValues rec = null;
String[] args={params[0].toString()};
if (BuildConfig.DEBUG) {
Log.v (TAG, "GetRecordTask: doInBackground args[0]: " + args[0]);
}
Cursor c = getReadableDatabase().query(TABLE,new String[] {ID,DATE,SYSTOLIC,NOTES,DIASTOLIC,PULSE,LOCATION,SIDE},"_id = ?",args,null,null,null,"1");
c.moveToFirst();
if (! c.isAfterLast()) {
rec = new ContentValues();
rec.put(ID,c.getLong(0));
rec.put(DATE,c.getLong(1));
rec.put(SYSTOLIC,c.getInt(2));
rec.put(NOTES,c.getString(3));
rec.put(DIASTOLIC,c.getInt(4));
rec.put(PULSE,c.getInt(5));
rec.put(LOCATION,c.getInt(6));
rec.put(SIDE,c.getInt(7));
if (BuildConfig.DEBUG) {
Log.v (TAG,"doInBackground returning: " + rec.getAsString(SYSTOLIC) + " " + rec.getAsString(NOTES));
}
}
c.close();
return (rec);
}
@Override
public void onPostExecute(ContentValues rec) {
listener.setRecord(rec);
}
}
public Long SaveRecordAsync(RecordListener listener,ContentValues rec) {
Long serialNo = getNewSerialNo();
new SaveRecordTask(listener,serialNo).execute(rec);
return serialNo;
}
private class SaveRecordTask extends AsyncTask<ContentValues, Void, Long> {
private RecordListener listener = null;
private Long serialNo;
SaveRecordTask(RecordListener listener,Long serialNo) {
this.listener=listener;
this.serialNo = serialNo;
}
@Override
protected Long doInBackground(ContentValues... params) {
ContentValues vals;
Long result;
vals = params[0];
if (vals.getAsLong(ID) == 0) {
if (BuildConfig.DEBUG) {
Log.v (TAG, "SaveRecordTask: new record");
}
vals.remove(ID);
result = getWritableDatabase().insert(TABLE,null,vals);
} else {
if (BuildConfig.DEBUG) {
Log.v (TAG, "SaveRecordTask: update record");
}
result = getWritableDatabase().replace(TABLE,null,vals);
}
if (BuildConfig.DEBUG) {
Log.v(TAG,"Finished insert/replace: result = " + result.toString());
}
return(result);
}
@Override
public void onPostExecute(Long id) {
if (listener != null) {
listener.setId(id);
}
for (RecordChangedListener lnr: recordChangedListeners) {
lnr.recordChanged(id);
}
}
}
void deleteRecordAsync(Long id) {
new DeleteRecordTask().execute(id);
}
private class DeleteRecordTask extends AsyncTask<Long, Void, Long> {
@Override
protected Long doInBackground(Long... params) {
Long id = params[0];
String[] arg={id.toString()};
getWritableDatabase().delete(TABLE,"_id = ?",arg);
return(id);
}
@Override
public void onPostExecute(Long id) {
if (BuildConfig.DEBUG) {
Log.v (TAG,"DeleteRecordTask: onPostExecute");
}
for (RecordChangedListener lnr: recordChangedListeners) {
if (BuildConfig.DEBUG) {
Log.v (TAG,"DeleteRecordTask: onPostExecute: sending notification");
}
lnr.recordChanged(id);
}
}
}
private Long getNewSerialNo() {
requestSerialNo++;
if (BuildConfig.DEBUG) {
Log.v(TAG,"getNewSerialNo: issued new serial number: " + requestSerialNo);
}
return requestSerialNo;
}
/**
* loads a set of testing data -- should only be used when debuging
*/
private void loadTestData(SQLiteDatabase db) {
Log.v (TAG, "Loading test data");
db.execSQL("INSERT INTO heart VALUES(5,1336935642820,138,'',94,70,1,1);");
db.execSQL("INSERT INTO heart VALUES(6,1331591422458,138,'shoppers',95,68,1,1);");
db.execSQL("INSERT INTO heart VALUES(7,1332031518069,141,'',81,77,1,1);");
db.execSQL("INSERT INTO heart VALUES(8,1332113453882,138,'',79,62,1,1);");
db.execSQL("INSERT INTO heart VALUES(9,1332372643664,130,'',80,68,0,1);");
db.execSQL("INSERT INTO heart VALUES(10,1334090149590,139,'',94,74,1,1);");
db.execSQL("INSERT INTO heart VALUES(11,1334090706629,145,'save-on',92,78,1,1);");
db.execSQL("INSERT INTO heart VALUES(12,1335136515538,140,'London drugs',87,69,1,1);");
db.execSQL("INSERT INTO heart VALUES(13,1338263140748,133,'save-on',91,65,1,1);");
db.execSQL("INSERT INTO heart VALUES(14,1341279953183,126,'',84,64,1,1);");
db.execSQL("INSERT INTO heart VALUES(15,1344639656326,120,'',85,65,1,1);");
db.execSQL("INSERT INTO heart VALUES(16,1345402833499,124,'',84,59,1,1);");
db.execSQL("INSERT INTO heart VALUES(17,1346094002525,130,'',90,65,1,1);");
db.execSQL("INSERT INTO heart VALUES(18,1348540232095,133,'',84,77,1,1);");
db.execSQL("INSERT INTO heart VALUES(19,1354492804768,126,'home',80,63,1,0);");
db.execSQL("INSERT INTO heart VALUES(20,1355453153722,135,'save on @ 203 st',96,70,1,1);");
db.execSQL("INSERT INTO heart VALUES(21,1356235604625,135,'home',87,71,1,1);");
db.execSQL("INSERT INTO heart VALUES(22,1356310832056,137,'',89,67,1,1);");
}
}