Moved export data functionality to an AsyncTask to avoid ApplicationNotResponding warnings when exporting large amounts of data.
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:versionCode="126"
|
android:versionCode="127"
|
||||||
android:versionName="4.1.11">
|
android:versionName="4.1.12">
|
||||||
<!-- android:allowBackup="false" -->
|
<!-- android:allowBackup="false" -->
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class LogManager {
|
|||||||
public double mNDATimeRemaining; // hours
|
public double mNDATimeRemaining; // hours
|
||||||
public double mNDALogPeriodHours = 24.0; // hours
|
public double mNDALogPeriodHours = 24.0; // hours
|
||||||
private static Context mContext;
|
private static Context mContext;
|
||||||
private OsdUtil mUtil;
|
private static OsdUtil mUtil;
|
||||||
public static WebApiConnection mWac;
|
public static WebApiConnection mWac;
|
||||||
public static final boolean USE_FIREBASE_BACKEND = false;
|
public static final boolean USE_FIREBASE_BACKEND = false;
|
||||||
|
|
||||||
@@ -493,109 +493,23 @@ public class LogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* exportToFile - export datapoints data to a csv file on the android device.
|
* exportToCsvFile - export datapoints data to a csv file on the android device.
|
||||||
*
|
*
|
||||||
* @param endDate end date of period to export (Date type)
|
* @param endDate end date of period to export (Date type)
|
||||||
* @param duration duration in hours of period to export (double)
|
* @param duration duration in hours of period to export (double)
|
||||||
* @param uri uri of file to save.
|
* @param uri uri of file to save.
|
||||||
|
* @param callback function to be called on completion of the task (returns true on success, false on error)
|
||||||
*/
|
*/
|
||||||
public void exportToCsvFile(Date endDate, double duration, Uri uri, BooleanCallback callback) {
|
public void exportToCsvFile(Date endDate, double duration, Uri uri, BooleanCallback callback) {
|
||||||
Log.v(TAG, "exportToCsvFile(): uri=" + uri.toString());
|
Log.v(TAG, "exportToCsvFile(): uri=" + uri.toString());
|
||||||
long endDateMillis = endDate.getTime();
|
new ExportDataTask(endDate, duration, uri, (boolean retVal) -> {
|
||||||
long durationMillis = (long) (duration * 3600. * 1000);
|
Log.v(TAG, "exportToCsvFile - returned " + retVal);
|
||||||
long startDateMillis = endDateMillis - durationMillis;
|
callback.accept(retVal);
|
||||||
Log.v(TAG, "exportToCsvFile() - endDateMillis=" + endDateMillis + ", startDateMillis=" + startDateMillis + ", durationMillis=" + durationMillis);
|
|
||||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
||||||
String sDateStr = dateFormat.format(new Date(startDateMillis));
|
|
||||||
String eDateStr = dateFormat.format(new Date(endDateMillis));
|
|
||||||
Log.v(TAG, "exportToFile() - sDateStr=" + sDateStr + " eDateStr=" + eDateStr);
|
|
||||||
String[] columns = {"*"};
|
|
||||||
String whereClause = "DataTime>? AND DataTime<?";
|
|
||||||
String[] whereArgs = {sDateStr, eDateStr};
|
|
||||||
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs,
|
|
||||||
null, null, "dataTime DESC", (Cursor cursor) -> {
|
|
||||||
Log.v(TAG, "exportToCsvFile - returned " + cursor);
|
|
||||||
if (cursor != null) {
|
|
||||||
Log.d(TAG, "we got a cursor!");
|
|
||||||
try {
|
|
||||||
ParcelFileDescriptor pfd = mContext.getContentResolver().
|
|
||||||
openFileDescriptor(uri, "w");
|
|
||||||
FileOutputStream fileOutputStream =
|
|
||||||
new FileOutputStream(pfd.getFileDescriptor());
|
|
||||||
fileOutputStream.write(("# dataTime, alarmState, hr, o2sat, accel*125\n").getBytes());
|
|
||||||
writeDatapointsToFile(cursor, fileOutputStream);
|
|
||||||
// Let the document provider know you're done by closing the stream.
|
|
||||||
fileOutputStream.close();
|
|
||||||
pfd.close();
|
|
||||||
callback.accept(true);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
mUtil.showToast(mContext.getString(R.string.error_exporting_data));
|
|
||||||
Log.e(TAG, "exportToFile() - FileNotFoundException: " + e.toString());
|
|
||||||
callback.accept(false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
mUtil.showToast(mContext.getString(R.string.error_exporting_data));
|
|
||||||
Log.e(TAG, "exportToFile() - IOException: " + e.toString());
|
|
||||||
callback.accept(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "exportToCsvFile() - returned null result");
|
|
||||||
callback.accept(false);
|
|
||||||
}
|
|
||||||
}).execute();
|
}).execute();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeDatapointsToFile(Cursor c, FileOutputStream fileOutputStream) {
|
|
||||||
Log.v(TAG, "writeDatapointsToFile()");
|
|
||||||
JSONArray dataObj;
|
|
||||||
String dataJsonStr;
|
|
||||||
JSONObject dataJsonObj;
|
|
||||||
JSONArray rawDataArr;
|
|
||||||
Log.d(TAG,"writeDatapointsToFile()" + c.getColumnNames());
|
|
||||||
//for (int i=0;i<c.getColumnCount();i++) {
|
|
||||||
// Log.d(TAG," Column"+i+" = "+c.getColumnName(i));
|
|
||||||
//}
|
|
||||||
try {
|
|
||||||
Log.d(TAG,"writeDatapointsToFile() - writing query result to csv file....");
|
|
||||||
while (c.moveToNext()) {
|
|
||||||
//Log.d(TAG,"writeDatapointsToFile - row="+c.getString(0)+", "+c.getString(1));
|
|
||||||
dataJsonStr = c.getString(3); // dataJSON is index 3
|
|
||||||
//Log.v(TAG, "exportToFile() - i=" + i + "dataJsonStr=" + dataJsonStr);
|
|
||||||
dataJsonObj = new JSONObject(dataJsonStr);
|
|
||||||
rawDataArr = dataJsonObj.getJSONArray("rawData");
|
|
||||||
try {
|
|
||||||
//fileOutputStream.write(dataJsonObj.getString("dataTime").getBytes(StandardCharsets.UTF_8));
|
|
||||||
fileOutputStream.write(c.getString(1).getBytes(StandardCharsets.UTF_8)); // We use the database record date rather than datajson date because it is formatted yyyy-mm-dd
|
|
||||||
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
|
||||||
fileOutputStream.write(dataJsonObj.getString("alarmState").getBytes(StandardCharsets.UTF_8));
|
|
||||||
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
|
||||||
fileOutputStream.write(dataJsonObj.getString("hr").getBytes(StandardCharsets.UTF_8));
|
|
||||||
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
|
||||||
fileOutputStream.write(dataJsonObj.getString("o2Sat").getBytes(StandardCharsets.UTF_8));
|
|
||||||
for (int j = 0; j < 125; j++) { // FIXME Hard Coded array length, but rawDataArr.length() is 125*3 so we don't want to use that.
|
|
||||||
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
|
||||||
fileOutputStream.write(rawDataArr.getString(j).getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
fileOutputStream.write("\n".getBytes(StandardCharsets.UTF_8));
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "exportToFile() - ERROR Writing File: " + e.toString());
|
|
||||||
mUtil.showToast("ERROR WRITING FILE");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Log.d(TAG,"writeDatapointsToFile() - data written to file ok");
|
|
||||||
mUtil.showToast(mContext.getString(R.string.data_exported_ok));
|
|
||||||
|
|
||||||
} catch (JSONException | NullPointerException e) {
|
|
||||||
Log.v(TAG, "createEventCallback(): Error Creating JSON Object from string ");
|
|
||||||
dataObj = null;
|
|
||||||
mUtil.showToast(mContext.getString(R.string.error_exporting_data));
|
|
||||||
Log.e(TAG, "exportToFile() - JSONException: " + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -877,6 +791,158 @@ public class LogManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//query(String table, String[] columns, String selection, String[] selectionArgs,
|
||||||
|
// String groupBy, String having, String orderBy)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the contents of the local datapoints table between given dates
|
||||||
|
* to a .csv file.
|
||||||
|
* Use as new ExportDataTask(xxx,xxx,xx,xxxx).execute()
|
||||||
|
*/
|
||||||
|
static private class ExportDataTask extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
BooleanCallback mCallback;
|
||||||
|
Date mEndDate;
|
||||||
|
double mDuration;
|
||||||
|
Uri mUri;
|
||||||
|
|
||||||
|
|
||||||
|
ExportDataTask(Date endDate, double duration, Uri uri, BooleanCallback callback) {
|
||||||
|
Log.i(TAG,"ExportDataTask constructor()");
|
||||||
|
this.mCallback = callback;
|
||||||
|
mEndDate = endDate;
|
||||||
|
mDuration = duration;
|
||||||
|
mUri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
Log.v(TAG, "ExportDataTask.doInBackground()");
|
||||||
|
long endDateMillis = mEndDate.getTime();
|
||||||
|
long durationMillis = (long) (mDuration * 3600. * 1000);
|
||||||
|
long startDateMillis = endDateMillis - durationMillis;
|
||||||
|
Log.v(TAG, "exportDataTask() - endDateMillis=" + endDateMillis + ", startDateMillis=" + startDateMillis + ", durationMillis=" + durationMillis);
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
String sDateStr = dateFormat.format(new Date(startDateMillis));
|
||||||
|
String eDateStr = dateFormat.format(new Date(endDateMillis));
|
||||||
|
Log.v(TAG, "ExportDataTask.doInBackground - sDateStr=" + sDateStr + " eDateStr=" + eDateStr);
|
||||||
|
String[] columns = {"*"};
|
||||||
|
String whereClause = "DataTime>? AND DataTime<?";
|
||||||
|
String[] whereArgs = {sDateStr, eDateStr};
|
||||||
|
|
||||||
|
try {
|
||||||
|
Cursor cursor = mOsdDb.query(mDpTableName, columns, whereClause,
|
||||||
|
whereArgs, null, null, "dataTime DESC");
|
||||||
|
cursor.moveToFirst();
|
||||||
|
|
||||||
|
Log.v(TAG, "ExportDataTask.doInBackground() - returned " + cursor);
|
||||||
|
if (cursor != null) {
|
||||||
|
Log.d(TAG, "ExportDataTask.doInBackground() - query complete - writing to file....");
|
||||||
|
try {
|
||||||
|
ParcelFileDescriptor pfd = mContext.getContentResolver().
|
||||||
|
openFileDescriptor(mUri, "w");
|
||||||
|
FileOutputStream fileOutputStream =
|
||||||
|
new FileOutputStream(pfd.getFileDescriptor());
|
||||||
|
fileOutputStream.write(("# dataTime, alarmState, hr, o2sat, accel*125\n").getBytes());
|
||||||
|
int nRec = writeDatapointsToFile(cursor, fileOutputStream);
|
||||||
|
// Let the document provider know you're done by closing the stream.
|
||||||
|
fileOutputStream.close();
|
||||||
|
pfd.close();
|
||||||
|
Log.d(TAG, "ExportDataTask.doInBackground() - file written ok - notifying callback function....");
|
||||||
|
mCallback.accept(true);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
mUtil.showToast(mContext.getString(R.string.error_exporting_data));
|
||||||
|
Log.e(TAG, "ExportDataTask.doInBackground() - FileNotFoundException: " + e.toString());
|
||||||
|
mCallback.accept(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
mUtil.showToast(mContext.getString(R.string.error_exporting_data));
|
||||||
|
Log.e(TAG, "ExportDataTask.doInBackground() - IOException: " + e.toString());
|
||||||
|
mCallback.accept(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "ExportDataTask.doInBackground() - returned null result");
|
||||||
|
mCallback.accept(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
Log.e(TAG, "ExportDataTask.doInBackground(): Error selecting Data: " + e.toString());
|
||||||
|
return (null);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.e(TAG, "ExportDataTask.doInBackground(): Illegal Argument Exception: " + e.toString());
|
||||||
|
return (null);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
Log.e(TAG, "SelectQueryTask.doInBackground(): Null Pointer Exception: " + e.toString());
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(final Boolean result) {
|
||||||
|
Log.i(TAG,"ExportDataTask.onPostExecute() - notifying callback function of result: "+result);
|
||||||
|
mCallback.accept(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int writeDatapointsToFile(Cursor c, FileOutputStream fileOutputStream) {
|
||||||
|
Log.v(TAG, "writeDatapointsToFile()");
|
||||||
|
int nRec = 0;
|
||||||
|
JSONArray dataObj;
|
||||||
|
String dataJsonStr;
|
||||||
|
JSONObject dataJsonObj;
|
||||||
|
JSONArray rawDataArr;
|
||||||
|
Log.d(TAG, "writeDatapointsToFile()" + c.getColumnNames());
|
||||||
|
//for (int i=0;i<c.getColumnCount();i++) {
|
||||||
|
// Log.d(TAG," Column"+i+" = "+c.getColumnName(i));
|
||||||
|
//}
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "writeDatapointsToFile() - writing query result to csv file....");
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
nRec += 1;
|
||||||
|
//Log.d(TAG,"writeDatapointsToFile - row="+c.getString(0)+", "+c.getString(1));
|
||||||
|
dataJsonStr = c.getString(3); // dataJSON is index 3
|
||||||
|
//Log.v(TAG, "exportToFile() - i=" + i + "dataJsonStr=" + dataJsonStr);
|
||||||
|
dataJsonObj = new JSONObject(dataJsonStr);
|
||||||
|
rawDataArr = dataJsonObj.getJSONArray("rawData");
|
||||||
|
try {
|
||||||
|
//fileOutputStream.write(dataJsonObj.getString("dataTime").getBytes(StandardCharsets.UTF_8));
|
||||||
|
fileOutputStream.write(c.getString(1).getBytes(StandardCharsets.UTF_8)); // We use the database record date rather than datajson date because it is formatted yyyy-mm-dd
|
||||||
|
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
||||||
|
fileOutputStream.write(dataJsonObj.getString("alarmState").getBytes(StandardCharsets.UTF_8));
|
||||||
|
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
||||||
|
fileOutputStream.write(dataJsonObj.getString("hr").getBytes(StandardCharsets.UTF_8));
|
||||||
|
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
||||||
|
fileOutputStream.write(dataJsonObj.getString("o2Sat").getBytes(StandardCharsets.UTF_8));
|
||||||
|
for (int j = 0; j < 125; j++) { // FIXME Hard Coded array length, but rawDataArr.length() is 125*3 so we don't want to use that.
|
||||||
|
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
|
||||||
|
fileOutputStream.write(rawDataArr.getString(j).getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
fileOutputStream.write("\n".getBytes(StandardCharsets.UTF_8));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "exportToFile() - ERROR Writing File: " + e.toString());
|
||||||
|
mUtil.showToast("ERROR WRITING FILE");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Log.d(TAG, "writeDatapointsToFile() - data written to file ok");
|
||||||
|
mUtil.showToast(mContext.getString(R.string.data_exported_ok)+ " "+nRec);
|
||||||
|
return nRec;
|
||||||
|
|
||||||
|
} catch (JSONException | NullPointerException e) {
|
||||||
|
Log.v(TAG, "createEventCallback(): Error Creating JSON Object from string ");
|
||||||
|
dataObj = null;
|
||||||
|
mUtil.showToast(mContext.getString(R.string.error_exporting_data));
|
||||||
|
Log.e(TAG, "exportToFile() - JSONException: " + e.toString());
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getEventWhereClause(boolean includeWarnings) {
|
private String getEventWhereClause(boolean includeWarnings) {
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user