Merge branch 'V4.1.x' into V4.2.x
This commit is contained in:
Binary file not shown.
@@ -1,8 +1,9 @@
|
|||||||
<?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="125"
|
android:versionCode="128"
|
||||||
android:versionName="4.2.1a">
|
android:versionName="4.2.1b">
|
||||||
|
|
||||||
<!-- 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" />
|
||||||
@@ -22,6 +23,10 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
|
|
||||||
|
<!-- AD_ID seems to be added automatically as part of google play services. We don't use ads,
|
||||||
|
so explicitly remove it to keep Google Play store happy. -->
|
||||||
|
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove"/>
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.telephony"
|
android:name="android.hardware.telephony"
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
|
|||||||
@@ -209,17 +209,34 @@ public class ExportDataActivity extends AppCompatActivity
|
|||||||
// mDateTxt.getText().toString(), mTimeTxt.getText().toString(), mDuration));
|
// mDateTxt.getText().toString(), mTimeTxt.getText().toString(), mDuration));
|
||||||
Log.d(TAG, String.format("EndDate=%s %s, Duration=%3.1f hrs",
|
Log.d(TAG, String.format("EndDate=%s %s, Duration=%3.1f hrs",
|
||||||
mDateTxt.getText().toString(), mTimeTxt.getText().toString(), mDuration));
|
mDateTxt.getText().toString(), mTimeTxt.getText().toString(), mDuration));
|
||||||
ProgressBar pb = (ProgressBar) findViewById(R.id.exportPb);
|
|
||||||
pb.setIndeterminate(true);
|
|
||||||
pb.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
mExportBtn.setEnabled(false);
|
showProgressBar();
|
||||||
mExportBtn.setVisibility(View.INVISIBLE);
|
|
||||||
this.openFile();
|
this.openFile();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showProgressBar() {
|
||||||
|
ProgressBar pb = (ProgressBar) findViewById(R.id.exportPb);
|
||||||
|
pb.setIndeterminate(true);
|
||||||
|
pb.setVisibility(View.VISIBLE);
|
||||||
|
mExportBtn.setEnabled(false);
|
||||||
|
mExportBtn.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideProgressBar() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
ProgressBar pb = (ProgressBar) findViewById(R.id.exportPb);
|
||||||
|
pb.setIndeterminate(true);
|
||||||
|
pb.setVisibility(View.INVISIBLE);
|
||||||
|
mExportBtn.setEnabled(true);
|
||||||
|
mExportBtn.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void openFile() {
|
private void openFile() {
|
||||||
|
|
||||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
@@ -248,96 +265,14 @@ public class ExportDataActivity extends AppCompatActivity
|
|||||||
// Perform operations on the document using its URI.
|
// Perform operations on the document using its URI.
|
||||||
//mUtil.showToast("URI="+uri.toString());
|
//mUtil.showToast("URI="+uri.toString());
|
||||||
Log.v(TAG, "onActivityResult() - exporting to file " + uri.toString());
|
Log.v(TAG, "onActivityResult() - exporting to file " + uri.toString());
|
||||||
exportToFile(uri);
|
mLm.exportToCsvFile(mEndDate, mDuration,uri, (boolean b)-> {
|
||||||
|
Log.v(TAG,"onActivityResult callback");
|
||||||
|
hideProgressBar();
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, resultData);
|
super.onActivityResult(requestCode, resultCode, resultData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exportToFile(Uri uri) {
|
|
||||||
Log.v(TAG, "exportToFile(): uri=" + uri.toString());
|
|
||||||
long endDateMillis = mEndDate.getTime();
|
|
||||||
long durationMillis = (long) (mDuration * 3600. * 1000);
|
|
||||||
long startDateMillis = endDateMillis - durationMillis;
|
|
||||||
Log.v(TAG, "exportToFile() - 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);
|
|
||||||
mLm.getDatapointsByDate(
|
|
||||||
sDateStr, eDateStr, (String datapointsJsonStr) -> {
|
|
||||||
Log.v(TAG, "exportToFile() - datapoints=" + datapointsJsonStr);
|
|
||||||
// Open file for writing
|
|
||||||
try {
|
|
||||||
ParcelFileDescriptor pfd = this.getContentResolver().
|
|
||||||
openFileDescriptor(uri, "w");
|
|
||||||
FileOutputStream fileOutputStream =
|
|
||||||
new FileOutputStream(pfd.getFileDescriptor());
|
|
||||||
fileOutputStream.write(("# dataTime, alarmState, hr, o2sat, accel*125\n").getBytes());
|
|
||||||
JSONArray dataObj;
|
|
||||||
try {
|
|
||||||
dataObj = new JSONArray(datapointsJsonStr);
|
|
||||||
Log.v(TAG, "exportToFile() - dataObj length=" + dataObj.length());
|
|
||||||
for (int i = 0; i < dataObj.length(); i++) {
|
|
||||||
JSONObject datapointJsonObj = dataObj.getJSONObject(i);
|
|
||||||
String dataJsonStr = datapointJsonObj.getString("dataJSON");
|
|
||||||
Log.v(TAG, "exportToFile() - i=" + i + "dataJsonStr=" + dataJsonStr);
|
|
||||||
JSONObject dataJsonObj = new JSONObject(dataJsonStr);
|
|
||||||
JSONArray rawDataArr = dataJsonObj.getJSONArray("rawData");
|
|
||||||
try {
|
|
||||||
fileOutputStream.write(dataJsonObj.getString("dataTime").getBytes(StandardCharsets.UTF_8));
|
|
||||||
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 < rawDataArr.length(); j++) {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (JSONException | NullPointerException e) {
|
|
||||||
Log.v(TAG, "createEventCallback(): Error Creating JSON Object from string " + datapointsJsonStr);
|
|
||||||
dataObj = null;
|
|
||||||
mUtil.showToast(getString(R.string.error_exporting_data));
|
|
||||||
Log.e(TAG, "exportToFile() - JSONException: " + e.toString());
|
|
||||||
}
|
|
||||||
// Let the document provider know you're done by closing the stream.
|
|
||||||
fileOutputStream.close();
|
|
||||||
pfd.close();
|
|
||||||
mUtil.showToast(getString(R.string.data_exported_ok));
|
|
||||||
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
mUtil.showToast(getString(R.string.error_exporting_data));
|
|
||||||
Log.e(TAG, "exportToFile() - FileNotFoundException: " + e.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
mUtil.showToast(getString(R.string.error_exporting_data));
|
|
||||||
Log.e(TAG, "exportToFile() - IOException: " + e.toString());
|
|
||||||
}
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
ProgressBar pb = (ProgressBar) findViewById(R.id.exportPb);
|
|
||||||
pb.setIndeterminate(true);
|
|
||||||
pb.setVisibility(View.INVISIBLE);
|
|
||||||
mExportBtn.setEnabled(true);
|
|
||||||
mExportBtn.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,17 +30,25 @@ import android.database.DatabaseUtils;
|
|||||||
import android.database.SQLException;
|
import android.database.SQLException;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.CountDownTimer;
|
import android.os.CountDownTimer;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.text.format.Time;
|
import android.text.format.Time;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -66,7 +74,7 @@ import java.util.HashMap;
|
|||||||
* - Query the local database to return all datapoints within +/- EventDuration/2 minutes of the event.
|
* - Query the local database to return all datapoints within +/- EventDuration/2 minutes of the event.
|
||||||
* - Upload the datapoints, linking them to the new eventID.
|
* - Upload the datapoints, linking them to the new eventID.
|
||||||
* - Mark all the uploaded datapoints as uploaded.
|
* - Mark all the uploaded datapoints as uploaded.
|
||||||
*
|
* <p>
|
||||||
* Event statuses:
|
* Event statuses:
|
||||||
* 0 - OK
|
* 0 - OK
|
||||||
* 1 - WARNING
|
* 1 - WARNING
|
||||||
@@ -75,7 +83,7 @@ import java.util.HashMap;
|
|||||||
* 4 - FAULT
|
* 4 - FAULT
|
||||||
* 5 - Manual Alarm
|
* 5 - Manual Alarm
|
||||||
* 6 - NDA (Normal Daily Activities)
|
* 6 - NDA (Normal Daily Activities)
|
||||||
*
|
* <p>
|
||||||
* NDA Timer creates an event periodically to record Normal Daily Activities (NDA),
|
* NDA Timer creates an event periodically to record Normal Daily Activities (NDA),
|
||||||
* irrespective of the alarm state. This will upload a lot of data, so it will only run
|
* irrespective of the alarm state. This will upload a lot of data, so it will only run
|
||||||
* for 24 hours after being activated before shutting down requring the user to re-select
|
* for 24 hours after being activated before shutting down requring the user to re-select
|
||||||
@@ -97,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;
|
||||||
|
|
||||||
@@ -122,6 +130,11 @@ public class LogManager {
|
|||||||
void accept(ArrayList<HashMap<String, String>> retVal);
|
void accept(ArrayList<HashMap<String, String>> retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface BooleanCallback {
|
||||||
|
void accept(boolean retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public LogManager(Context context,
|
public LogManager(Context context,
|
||||||
boolean logRemote, boolean logRemoteMobile, String authToken,
|
boolean logRemote, boolean logRemoteMobile, String authToken,
|
||||||
long eventDuration, long remoteLogPeriod,
|
long eventDuration, long remoteLogPeriod,
|
||||||
@@ -242,21 +255,21 @@ public class LogManager {
|
|||||||
String val;
|
String val;
|
||||||
val = c.getString(c.getColumnIndex("id"));
|
val = c.getString(c.getColumnIndex("id"));
|
||||||
// We replace null values with empty string, otherwise they are completely excluded from output JSON.
|
// We replace null values with empty string, otherwise they are completely excluded from output JSON.
|
||||||
event.put("id", val==null ? "" : val );
|
event.put("id", val == null ? "" : val);
|
||||||
val = c.getString(c.getColumnIndex("dataTime"));
|
val = c.getString(c.getColumnIndex("dataTime"));
|
||||||
event.put("dataTime", val==null ? "" : val);
|
event.put("dataTime", val == null ? "" : val);
|
||||||
val = c.getString(c.getColumnIndex("status"));
|
val = c.getString(c.getColumnIndex("status"));
|
||||||
event.put("status", val==null ? "" : val);
|
event.put("status", val == null ? "" : val);
|
||||||
val = c.getString(c.getColumnIndex("type"));
|
val = c.getString(c.getColumnIndex("type"));
|
||||||
event.put("type", val==null ? "" : val);
|
event.put("type", val == null ? "" : val);
|
||||||
val = c.getString(c.getColumnIndex("subType"));
|
val = c.getString(c.getColumnIndex("subType"));
|
||||||
event.put("subType", val==null ? "" : val);
|
event.put("subType", val == null ? "" : val);
|
||||||
val = c.getString(c.getColumnIndex("notes"));
|
val = c.getString(c.getColumnIndex("notes"));
|
||||||
event.put("desc", val==null ? "" : val);
|
event.put("desc", val == null ? "" : val);
|
||||||
val = c.getString(c.getColumnIndex("dataJSON"));
|
val = c.getString(c.getColumnIndex("dataJSON"));
|
||||||
event.put("dataJSON", val==null ? "" : val);
|
event.put("dataJSON", val == null ? "" : val);
|
||||||
val = c.getString(c.getColumnIndex("uploaded"));
|
val = c.getString(c.getColumnIndex("uploaded"));
|
||||||
event.put("uploaded", val==null ? "" : val);
|
event.put("uploaded", val == null ? "" : val);
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
eventsArray.put(i, event);
|
eventsArray.put(i, event);
|
||||||
i++;
|
i++;
|
||||||
@@ -342,7 +355,7 @@ public class LogManager {
|
|||||||
|
|
||||||
if (sdData.alarmState != 0) {
|
if (sdData.alarmState != 0) {
|
||||||
Log.i(TAG, "writeDatapointToLocalDb(): adding event to local DB");
|
Log.i(TAG, "writeDatapointToLocalDb(): adding event to local DB");
|
||||||
createLocalEvent(dateStr,sdData.alarmState,null, null, null, sdData.toSettingsJSON());
|
createLocalEvent(dateStr, sdData.alarmState, null, null, null, sdData.toSettingsJSON());
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
Log.e(TAG, "writeToLocalDb(): Error Writing Data: " + e.toString());
|
Log.e(TAG, "writeToLocalDb(): Error Writing Data: " + e.toString());
|
||||||
@@ -358,18 +371,18 @@ public class LogManager {
|
|||||||
|
|
||||||
public boolean createLocalEvent(String dataTime, long status, String type, String subType, String desc, String dataJSON) {
|
public boolean createLocalEvent(String dataTime, long status, String type, String subType, String desc, String dataJSON) {
|
||||||
// Expects dataTime to be in format: SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
// Expects dataTime to be in format: SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
Log.d(TAG, "createLocalEvent() - dataTime=" + dataTime + ", status=" + status + ", dataJSON="+dataJSON);
|
Log.d(TAG, "createLocalEvent() - dataTime=" + dataTime + ", status=" + status + ", dataJSON=" + dataJSON);
|
||||||
// Write Event to database
|
// Write Event to database
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put("dataTime", dataTime);
|
values.put("dataTime", dataTime);
|
||||||
values.put("status", status);
|
values.put("status", status);
|
||||||
values.put("type", type);
|
values.put("type", type);
|
||||||
values.put("subType",subType);
|
values.put("subType", subType);
|
||||||
values.put("notes",desc);
|
values.put("notes", desc);
|
||||||
values.put("dataJSON", dataJSON);
|
values.put("dataJSON", dataJSON);
|
||||||
|
|
||||||
long newRowId = mOsdDb.insert(mEventsTableName, null, values);
|
long newRowId = mOsdDb.insert(mEventsTableName, null, values);
|
||||||
Log.d(TAG, "createLocalEvent(): Created Row ID"+newRowId);
|
Log.d(TAG, "createLocalEvent(): Created Row ID" + newRowId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,13 +485,32 @@ public class LogManager {
|
|||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
callback.accept(cursor2Json(cursor));
|
callback.accept(cursor2Json(cursor));
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG,"getDatapointsByDate() - returned null result");
|
Log.w(TAG, "getDatapointsByDate() - returned null result");
|
||||||
callback.accept(null);
|
callback.accept(null);
|
||||||
}
|
}
|
||||||
}).execute();
|
}).execute();
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* exportToCsvFile - export datapoints data to a csv file on the android device.
|
||||||
|
*
|
||||||
|
* @param endDate end date of period to export (Date type)
|
||||||
|
* @param duration duration in hours of period to export (double)
|
||||||
|
* @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) {
|
||||||
|
Log.v(TAG, "exportToCsvFile(): uri=" + uri.toString());
|
||||||
|
new ExportDataTask(endDate, duration, uri, (boolean retVal) -> {
|
||||||
|
Log.v(TAG, "exportToCsvFile - returned " + retVal);
|
||||||
|
callback.accept(retVal);
|
||||||
|
}).execute();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an array list of objects representing the events in the database by calling the specified callback function.
|
* Return an array list of objects representing the events in the database by calling the specified callback function.
|
||||||
@@ -486,7 +518,8 @@ public class LogManager {
|
|||||||
* @param includeWarnings - whether to include warnings in the list of events, or just alarm conditions.
|
* @param includeWarnings - whether to include warnings in the list of events, or just alarm conditions.
|
||||||
* @return True on successful start or false if call fails.
|
* @return True on successful start or false if call fails.
|
||||||
*/
|
*/
|
||||||
public boolean getEventsList(boolean includeWarnings, ArrayListCallback callback) {
|
public boolean getEventsList(boolean includeWarnings, ArrayListCallback
|
||||||
|
callback) {
|
||||||
Log.v(TAG, "getEventsList - includeWarnings=" + includeWarnings);
|
Log.v(TAG, "getEventsList - includeWarnings=" + includeWarnings);
|
||||||
ArrayList<HashMap<String, String>> eventsList = new ArrayList<>();
|
ArrayList<HashMap<String, String>> eventsList = new ArrayList<>();
|
||||||
|
|
||||||
@@ -572,7 +605,8 @@ public class LogManager {
|
|||||||
* @param includeWarnings - whether to include warnings in the list of events, or just alarm conditions.
|
* @param includeWarnings - whether to include warnings in the list of events, or just alarm conditions.
|
||||||
* @return True on successful start or false if call fails.
|
* @return True on successful start or false if call fails.
|
||||||
*/
|
*/
|
||||||
public boolean getNextEventToUpload(boolean includeWarnings, WebApiConnection.LongCallback callback) {
|
public boolean getNextEventToUpload(boolean includeWarnings, WebApiConnection.
|
||||||
|
LongCallback callback) {
|
||||||
Log.v(TAG, "getNextEventToUpload - includeWarnings=" + includeWarnings);
|
Log.v(TAG, "getNextEventToUpload - includeWarnings=" + includeWarnings);
|
||||||
|
|
||||||
String[] whereArgsStatus = getEventWhereArgs(includeWarnings);
|
String[] whereArgsStatus = getEventWhereArgs(includeWarnings);
|
||||||
@@ -619,7 +653,8 @@ public class LogManager {
|
|||||||
*
|
*
|
||||||
* @return True on successful start or false if call fails.
|
* @return True on successful start or false if call fails.
|
||||||
*/
|
*/
|
||||||
public boolean getNearestDatapointToDate(String dateStr, WebApiConnection.LongCallback callback) {
|
public boolean getNearestDatapointToDate(String
|
||||||
|
dateStr, WebApiConnection.LongCallback callback) {
|
||||||
Log.v(TAG, "getNextEventToDate - dateStr=" + dateStr);
|
Log.v(TAG, "getNextEventToDate - dateStr=" + dateStr);
|
||||||
String[] columns = {"*", "(julianday(dataTime)-julianday(datetime('" + dateStr + "'))) as ddiff"};
|
String[] columns = {"*", "(julianday(dataTime)-julianday(datetime('" + dateStr + "'))) as ddiff"};
|
||||||
//SQLStr = "SELECT *, (julianday(dataTime)-julianday(datetime('" + dateStr + "'))) as ddiff from " + mDbTableName + " order by ABS(ddiff) asc;";
|
//SQLStr = "SELECT *, (julianday(dataTime)-julianday(datetime('" + dateStr + "'))) as ddiff from " + mDbTableName + " order by ABS(ddiff) asc;";
|
||||||
@@ -652,7 +687,8 @@ public class LogManager {
|
|||||||
* @param includeWarnings - whether to include warnings in the list of events, or just alarm conditions.
|
* @param includeWarnings - whether to include warnings in the list of events, or just alarm conditions.
|
||||||
* @return True on successful start or false if call fails.
|
* @return True on successful start or false if call fails.
|
||||||
*/
|
*/
|
||||||
public boolean getLocalEventsCount(boolean includeWarnings, WebApiConnection.LongCallback callback) {
|
public boolean getLocalEventsCount(boolean includeWarnings, WebApiConnection.
|
||||||
|
LongCallback callback) {
|
||||||
//Log.v(TAG, "getLocalEventsCount- includeWarnings=" + includeWarnings);
|
//Log.v(TAG, "getLocalEventsCount- includeWarnings=" + includeWarnings);
|
||||||
String[] whereArgs = getEventWhereArgs(includeWarnings);
|
String[] whereArgs = getEventWhereArgs(includeWarnings);
|
||||||
String whereClause = getEventWhereClause(includeWarnings);
|
String whereClause = getEventWhereClause(includeWarnings);
|
||||||
@@ -755,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) {
|
||||||
/**
|
/**
|
||||||
@@ -967,7 +1155,8 @@ public class LogManager {
|
|||||||
for (int i = 0; i < dataObj.length(); i++) {
|
for (int i = 0; i < dataObj.length(); i++) {
|
||||||
mDatapointsToUploadList.add(dataObj.getJSONObject(i));
|
mDatapointsToUploadList.add(dataObj.getJSONObject(i));
|
||||||
}
|
}
|
||||||
} catch (JSONException | NullPointerException e) {
|
} catch (JSONException |
|
||||||
|
NullPointerException e) {
|
||||||
Log.v(TAG, "createEventCallback(): Error Creating JSON Object from string " + datapointsJsonStr);
|
Log.v(TAG, "createEventCallback(): Error Creating JSON Object from string " + datapointsJsonStr);
|
||||||
dataObj = null;
|
dataObj = null;
|
||||||
finishUpload();
|
finishUpload();
|
||||||
@@ -1122,7 +1311,7 @@ public class LogManager {
|
|||||||
timeNow.setToNow();
|
timeNow.setToNow();
|
||||||
long tNow = timeNow.toMillis(true);
|
long tNow = timeNow.toMillis(true);
|
||||||
long tDiffMillis = (tNow - mNDATimerStartTime);
|
long tDiffMillis = (tNow - mNDATimerStartTime);
|
||||||
mNDATimeRemaining = mNDALogPeriodHours - tDiffMillis / (3600.*1000.);
|
mNDATimeRemaining = mNDALogPeriodHours - tDiffMillis / (3600. * 1000.);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1183,11 +1372,11 @@ public class LogManager {
|
|||||||
|
|
||||||
|
|
||||||
public void createNDAEvent() {
|
public void createNDAEvent() {
|
||||||
Log.i(TAG,"createNDAEvent()");
|
Log.i(TAG, "createNDAEvent()");
|
||||||
Date curDate = new Date();
|
Date curDate = new Date();
|
||||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
String dateStr = dateFormat.format(curDate);
|
String dateStr = dateFormat.format(curDate);
|
||||||
createLocalEvent(dateStr,6,"nda", null, null,
|
createLocalEvent(dateStr, 6, "nda", null, null,
|
||||||
mSdSettingsData.toSettingsJSON());
|
mSdSettingsData.toSettingsJSON());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1196,7 +1385,6 @@ public class LogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class OsdDbHelper extends SQLiteOpenHelper {
|
public static class OsdDbHelper extends SQLiteOpenHelper {
|
||||||
// If you change the database schema, you must increment the database version.
|
// If you change the database schema, you must increment the database version.
|
||||||
public static final int DATABASE_VERSION = 1;
|
public static final int DATABASE_VERSION = 1;
|
||||||
@@ -1275,6 +1463,7 @@ public class LogManager {
|
|||||||
*/
|
*/
|
||||||
private class NDATimer extends CountDownTimer {
|
private class NDATimer extends CountDownTimer {
|
||||||
double mNDALogPeriodHours = 0;
|
double mNDALogPeriodHours = 0;
|
||||||
|
|
||||||
public NDATimer(long startTime, long interval, double logPeriod) {
|
public NDATimer(long startTime, long interval, double logPeriod) {
|
||||||
super(startTime, interval);
|
super(startTime, interval);
|
||||||
mNDALogPeriodHours = logPeriod;
|
mNDALogPeriodHours = logPeriod;
|
||||||
@@ -1295,7 +1484,7 @@ public class LogManager {
|
|||||||
timeNow.setToNow();
|
timeNow.setToNow();
|
||||||
long tNow = timeNow.toMillis(true);
|
long tNow = timeNow.toMillis(true);
|
||||||
long tDiffMillis = (tNow - mNDATimerStartTime);
|
long tDiffMillis = (tNow - mNDATimerStartTime);
|
||||||
double tDiffHrs = tDiffMillis / (3600.*1000.);
|
double tDiffHrs = tDiffMillis / (3600. * 1000.);
|
||||||
mNDATimeRemaining = mNDALogPeriodHours - tDiffHrs;
|
mNDATimeRemaining = mNDALogPeriodHours - tDiffHrs;
|
||||||
if (tDiffHrs >= mNDALogPeriodHours) {
|
if (tDiffHrs >= mNDALogPeriodHours) {
|
||||||
Log.i(TAG, "mNDATimer - onFinish - NDA logging period completed - switching off NDA Logging");
|
Log.i(TAG, "mNDATimer - onFinish - NDA logging period completed - switching off NDA Logging");
|
||||||
@@ -1307,8 +1496,8 @@ public class LogManager {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
} else {
|
} else {
|
||||||
// Restart this timer.
|
// Restart this timer.
|
||||||
Log.i(TAG,"NDATimer - tDiffMillis="+tDiffMillis+", tdiffHrs = "+tDiffHrs+ ", tnow="+tNow+", tstart="+mNDATimerStartTime+", NDALogPeriod="+mNDALogPeriodHours);
|
Log.i(TAG, "NDATimer - tDiffMillis=" + tDiffMillis + ", tdiffHrs = " + tDiffHrs + ", tnow=" + tNow + ", tstart=" + mNDATimerStartTime + ", NDALogPeriod=" + mNDALogPeriodHours);
|
||||||
Log.i(TAG,"NDATimer - re-starting NDA timer");
|
Log.i(TAG, "NDATimer - re-starting NDA timer");
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ public abstract class SdDataSource {
|
|||||||
private Time mHrStatusTime;
|
private Time mHrStatusTime;
|
||||||
private double mHrFrozenPeriod = 60; // seconds
|
private double mHrFrozenPeriod = 60; // seconds
|
||||||
private boolean mHrFrozenAlarm;
|
private boolean mHrFrozenAlarm;
|
||||||
|
private boolean mFidgetDetectorEnabled;
|
||||||
|
private double mFidgetPeriod;
|
||||||
|
private double mFidgetThreshold;
|
||||||
|
private Time mLastFidget = null;
|
||||||
|
|
||||||
|
|
||||||
public SdDataSource(Context context, Handler handler, SdDataReceiver sdDataReceiver) {
|
public SdDataSource(Context context, Handler handler, SdDataReceiver sdDataReceiver) {
|
||||||
@@ -723,6 +727,31 @@ public abstract class SdDataSource {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double calcRawDataStd(SdData sdData) {
|
||||||
|
/**
|
||||||
|
* Calculate the standard deviation in % of the rawData array in the SdData instance provided.
|
||||||
|
* It assumes that rawdata will contain 125 samples.
|
||||||
|
* Returns the standard deviation in %.
|
||||||
|
*/
|
||||||
|
// FIXME - assumes length of rawdata array is 125 data points
|
||||||
|
int j;
|
||||||
|
double sum = 0.0;
|
||||||
|
for (j = 0; j < 125; j++) { // FIXME - assumed length!
|
||||||
|
sum += sdData.rawData[j];
|
||||||
|
}
|
||||||
|
double mean = sum / 125;
|
||||||
|
|
||||||
|
double standardDeviation = 0.0;
|
||||||
|
for (j = 0; j < 125; j++) { // FIXME - assumed length!
|
||||||
|
standardDeviation += Math.pow(sdData.rawData[j] - mean, 2);
|
||||||
|
}
|
||||||
|
standardDeviation = Math.sqrt(standardDeviation / 125); // FIXME - assumed length!
|
||||||
|
|
||||||
|
// Convert standard deviation from milli-g to %
|
||||||
|
standardDeviation = 100. * standardDeviation / mean;
|
||||||
|
return (standardDeviation);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the status of the connection to the watch,
|
* Checks the status of the connection to the watch,
|
||||||
* and sets class variables for use by other functions.
|
* and sets class variables for use by other functions.
|
||||||
@@ -758,6 +787,23 @@ public abstract class SdDataSource {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mSdData.watchAppRunning = true;
|
mSdData.watchAppRunning = true;
|
||||||
|
|
||||||
|
// Check we have seen a fidget within the required period, or else assume a fault because watch is not being worn
|
||||||
|
if (mFidgetDetectorEnabled) {
|
||||||
|
if (mLastFidget == null) mLastFidget = tnow; // Initialise last fidget time on startup.
|
||||||
|
|
||||||
|
double accStd = calcRawDataStd(mSdData);
|
||||||
|
if (accStd > mFidgetThreshold) {
|
||||||
|
mLastFidget = tnow;
|
||||||
|
} else {
|
||||||
|
Log.d(TAG,"onStatus() - Fidget Detector - low movement - is watch being worn?");
|
||||||
|
tdiff = (tnow.toMillis(false) - mLastFidget.toMillis(false));
|
||||||
|
if (tdiff > (mFidgetPeriod) * 60 * 1000) {
|
||||||
|
Log.e(TAG, "onStatus() - Fidget Not Detected - is watch being worn?");
|
||||||
|
mSdDataReceiver.onSdDataFault(mSdData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have confirmation that the app is running, reset the
|
// if we have confirmation that the app is running, reset the
|
||||||
@@ -877,6 +923,20 @@ public abstract class SdDataSource {
|
|||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the Fidget Detector settings.
|
||||||
|
try {
|
||||||
|
mFidgetDetectorEnabled = SP.getBoolean("FidgetDetectorEnabled", false);
|
||||||
|
mFidgetPeriod = readDoublePref(SP, "FidgetDetectorPeriod", "20"); // minutes
|
||||||
|
Log.v(TAG, "updatePrefs() - mFidgetPeriod = " + mFidgetPeriod);
|
||||||
|
mFidgetThreshold = readDoublePref(SP, "FidgetDetectorThreshold", "0.6 ");
|
||||||
|
Log.d(TAG,"updatePrefs(): mFidgetThreshold="+mFidgetThreshold);
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.v(TAG, "updatePrefs() - Problem with FidgetDetector preferences!");
|
||||||
|
Toast toast = Toast.makeText(mContext, "Problem Parsing FidgetPeriod Preference", Toast.LENGTH_SHORT);
|
||||||
|
toast.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Watch Settings
|
// Watch Settings
|
||||||
String prefStr;
|
String prefStr;
|
||||||
|
|||||||
@@ -3,11 +3,7 @@
|
|||||||
<string name="app_name">OpenSeizureDetector</string>
|
<string name="app_name">OpenSeizureDetector</string>
|
||||||
<string name="changelog">
|
<string name="changelog">
|
||||||
"\n
|
"\n
|
||||||
\mV4.2.1 - Changed target Android Version to 13 (SDK33) (Play Store policy).
|
\nV4.2.1 - Added Machine Learning Model Manager so models can be updated easily.
|
||||||
\nV4.1.10 - Added warning if heart rate readings freeze and do not change for more than 1 minute.
|
|
||||||
\nV4.1.9 - Fixed problem with average heart rate alarm
|
|
||||||
Fixed issue with phone data source generating continuous alarms for Heart Rate or O2Sat
|
|
||||||
Fixed a small number of user reported issues (https://github.com/OpenSeizureDetector/Android_Pebble_SD/issues?q=is%3Aissue+milestone%3AV4.1.8)
|
|
||||||
"</string>
|
"</string>
|
||||||
<string name="UpgradeMsg">
|
<string name="UpgradeMsg">
|
||||||
Please enable the new <b>Data Sharing</b> feature to help improve OpenSeizureDetector!<br/>
|
Please enable the new <b>Data Sharing</b> feature to help improve OpenSeizureDetector!<br/>
|
||||||
@@ -509,6 +505,14 @@
|
|||||||
<string name="ml_sd_modelid_title">AI Model ID</string>
|
<string name="ml_sd_modelid_title">AI Model ID</string>
|
||||||
<string name="ml_sd_modelid_summary">ID number of machine learning (AI) model to be used - users should not edit this, but use the model Manager page instead.</string>
|
<string name="ml_sd_modelid_summary">ID number of machine learning (AI) model to be used - users should not edit this, but use the model Manager page instead.</string>
|
||||||
<string name="title_activity_ml_model_manager">MlModelManager</string>
|
<string name="title_activity_ml_model_manager">MlModelManager</string>
|
||||||
|
<string name="FidgetSettingsTitle">Fidget Detector Settings</string>
|
||||||
|
<string name="FidgetDetectorEnabledTitle">Enable Fidget Detector</string>
|
||||||
|
<string name="FidgetDetectorEnabledSummary">Generates a fault if no movement has been detected for a specified period (signifying the watch has been removed)</string>
|
||||||
|
<string name="FidgetDetectorThresholdSummary">The threshold (as % standard deviation) applied to each set of accelerometer data to determine if a \'Fidget\' has occurred.</string>
|
||||||
|
<string name="FidgetDetectorThresholdTitle">Fidget Detector Threshold (%)</string>
|
||||||
|
<string name="FidgetDetectorPeriodTitle">Fidget Detector Period (minutes)</string>
|
||||||
|
<string name="FidgetDetectorPeriodSummary">A fault is generated if no movement (fidgets) are detected for more than the Fidget Detector Period.</string>
|
||||||
|
|
||||||
<!-- Strings used for fragments for navigation -->
|
<!-- Strings used for fragments for navigation -->
|
||||||
<string name="first_fragment_label">First Fragment</string>
|
<string name="first_fragment_label">First Fragment</string>
|
||||||
<string name="second_fragment_label">Second Fragment</string>
|
<string name="second_fragment_label">Second Fragment</string>
|
||||||
|
|||||||
@@ -211,5 +211,22 @@
|
|||||||
android:title="@string/fall_window_title" />
|
android:title="@string/fall_window_title" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/FidgetSettingsTitle">
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="FidgetDetectorEnabled"
|
||||||
|
android:summary="@string/FidgetDetectorEnabledSummary"
|
||||||
|
android:title="@string/FidgetDetectorEnabledTitle" />
|
||||||
|
<EditTextPreference
|
||||||
|
android:defaultValue="0.6"
|
||||||
|
android:key="FidgetDetectorThreshold"
|
||||||
|
android:summary="@string/FidgetDetectorThresholdSummary"
|
||||||
|
android:title="@string/FidgetDetectorThresholdTitle" />
|
||||||
|
<EditTextPreference
|
||||||
|
android:defaultValue="20"
|
||||||
|
android:key="FidgetDetectorPeriod"
|
||||||
|
android:summary="@string/FidgetDetectorPeriodSummary"
|
||||||
|
android:title="@string/FidgetDetectorPeriodTitle" />
|
||||||
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|||||||
Reference in New Issue
Block a user