diff --git a/.gitignore b/.gitignore index 9ae0aa4..d5fb12a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ app/release/app-release.apk app/build app/app.iml app/release/output-metadata.json +*# diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index 43eceac..d782b42 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -73,6 +73,11 @@ import java.util.*; import android.text.format.Time; import com.rohitss.uceh.UCEHandler; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import uk.org.openseizuredetector.LogManager; /** @@ -101,6 +106,7 @@ public class SdServer extends Service implements SdDataReceiver { private int mCancelAudiblePeriod = 10; // Cancel Audible Period in minutes private long mCancelAudibleTimeRemaining = 0; private FaultTimer mFaultTimer = null; + private CheckUnvalidatedEventsTimer mEventsTimer = null; private int mFaultTimerPeriod = 30; // Fault Timer Period in sec private boolean mFaultTimerCompleted = false; @@ -323,6 +329,10 @@ public class SdServer extends Service implements SdDataReceiver { mUtil.writeToSysLogFile("SdServer.onStartCommand() - dataLog timer already running???"); } + if (mLogDataRemote) { + startValidatedEventsTimer(); + } + // Start the web server mUtil.writeToSysLogFile("SdServer.onStartCommand() - starting web server"); @@ -386,6 +396,12 @@ public class SdServer extends Service implements SdDataReceiver { mFaultTimer = null; } + // Stop the Event timer + if (mEventsTimer != null) { + Log.v(TAG, "onDestroy(): Cancelling events timer"); + stopValidatedEventsTimer(); + } + // Stop the Cancel Alarm Latch timer stopLatchTimer(); @@ -1507,13 +1523,13 @@ public class SdServer extends Service implements SdDataReceiver { public void stopFaultTimer() { if (mFaultTimer != null) { - Log.v(TAG, "stopFaultTimer(): fault timer already running - cancelling it."); + //Log.v(TAG, "stopFaultTimer(): fault timer already running - cancelling it."); mUtil.writeToSysLogFile("stopFaultTimer() - stopping fault timer"); mFaultTimer.cancel(); mFaultTimer = null; mFaultTimerCompleted = false; } else { - Log.v(TAG, "stopFaultTimer(): fault timer not running - not doing anything."); + //Log.v(TAG, "stopFaultTimer(): fault timer not running - not doing anything."); //mUtil.writeToSysLogFile("stopFaultTimer() - fault timer not running"); } } @@ -1545,4 +1561,130 @@ public class SdServer extends Service implements SdDataReceiver { } + + + /** + * Start the events timer. + */ + public void startValidatedEventsTimer() { + if (mEventsTimer != null) { + Log.v(TAG, "startValidatedEventsTimer(): timer already running - not doing anything."); + mUtil.writeToSysLogFile("startValidatedEventsTimer() - timer already running"); + } else { + Log.v(TAG, "startValidatedEventsTimer(): starting timer."); + mUtil.writeToSysLogFile("startValidatedEventsTimer() - starting timer"); + runOnUiThread(new Runnable() { + public void run() { + mEventsTimer = + // Run every 5 minutes (convert to ms.) + new CheckUnvalidatedEventsTimer(10 * 1000, 1000); + mEventsTimer.mIsRunning = true; + mEventsTimer.start(); + } + }); + } + } + + public void stopValidatedEventsTimer() { + if (mEventsTimer != null) { + Log.v(TAG, "stopEventsTimer(): timer already running - cancelling it."); + mUtil.writeToSysLogFile("stopEventsTimer() - stopping timer"); + mEventsTimer.cancel(); + mEventsTimer = null; + mEventsTimer.mIsRunning = false; + } else { + Log.v(TAG, "stopEventsTimer(): timer not running - not doing anything."); + } + } + + /** + * Periodically check if we have unvalidated events in the remote database. + * Show a notification if we do. + */ + private class CheckUnvalidatedEventsTimer extends CountDownTimer { + long mFirstUnvalidatedEvent; + public boolean mIsRunning = true; + public CheckUnvalidatedEventsTimer(long startTime, long interval) { + super(startTime, interval); + } + + @Override + public void onFinish() { + Log.v(TAG, "CheckUnvalidatedEventsTimer.onFinish()"); + // Retrieve events from remote database + mLm.mWac.getEvents((JSONObject remoteEventsObj) -> { + Log.v(TAG, "CheckUnvalidatedEventsTimer.onFinish.getEvents.Callback()"); + if (remoteEventsObj == null) { + Log.e(TAG, "CheckUnvalidatedEventsTimer.onFinish() Callback: Error Retrieving events"); + } else { + try { + JSONArray eventsArray = remoteEventsObj.getJSONArray("events"); + // A bit of a hack to display in reverse chronological order + mFirstUnvalidatedEvent = -1; + for (int i = eventsArray.length() - 1; i >= 0; i--) { + JSONObject eventObj = eventsArray.getJSONObject(i); + Long id = eventObj.getLong("id"); + String typeStr = eventObj.getString("type"); + //Log.v(TAG,"CheckUnvalidatedEventsTimer: id="+id+", typeStr="+typeStr); + if (typeStr.equals("null")) { + mFirstUnvalidatedEvent = id; + //Log.v(TAG,"CheckUnvalidatedEventsTimer:setting mFirstUnvalidatedEvent to "+mFirstUnvalidatedEvent); + } + } + Log.v(TAG,"CheckUnvalidatedEventsTimer.onFinish.callback - mFirstUnvalidatedEvent = "+ + mFirstUnvalidatedEvent); + if (mFirstUnvalidatedEvent >=0) { + showEventNotification(mFirstUnvalidatedEvent); + } else { + mNM.cancel(EVENT_NOTIFICATION_ID); + } + } catch (JSONException e) { + Log.e(TAG, "CheckUnvalidatedEventsTimer.onFinish(): Error Parsing remoteEventsObj: " + e.getMessage()); + //mUtil.showToast("Error Parsing remoteEventsObj - this should not happen!!!"); + } + } + }); + if (mIsRunning) { + // Restart this timer. + start(); + } + } + + @Override public void onTick(long msRemaining) { } + + /** + * Show a notification to tell the user that we have unvalidated events. + */ + private void showEventNotification(long eventId) { + Log.v(TAG, "showEventNotification()"); + int iconId; + String titleStr; + Uri soundUri = null; + iconId = R.drawable.star_of_life_query_24x24; + titleStr = getString(R.string.unvalidatedEventsTitle); + + Intent i = new Intent(getApplicationContext(), LogManagerControlActivity.class); + i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + PendingIntent contentIntent = + PendingIntent.getActivity(getApplicationContext(), + 0, i, PendingIntent.FLAG_UPDATE_CURRENT); + String contentStr = "Please Record your Seizure Events"; + if (mNotificationBuilder != null) { + mNotification = mNotificationBuilder.setContentIntent(contentIntent) + .setSmallIcon(iconId) + .setColor(0x00ffffff) + .setAutoCancel(false) + .setContentTitle(titleStr) + .setContentText(contentStr) + .setOnlyAlertOnce(true) + .build(); + mNM.notify(EVENT_NOTIFICATION_ID, mNotification); + } else { + Log.i(TAG, "showEventNotification() - notification builder is null, so not showing notification."); + } + } + + + } + } diff --git a/app/src/main/res/drawable/star_of_life_query_24x24.png b/app/src/main/res/drawable/star_of_life_query_24x24.png new file mode 100644 index 0000000..b362570 Binary files /dev/null and b/app/src/main/res/drawable/star_of_life_query_24x24.png differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e095b42..a8e3a5c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -326,4 +326,5 @@ ...waiting for data... Refresh Back + Unvalidated Seizure Events diff --git a/doc/Event_Log_Workflow.pptx b/doc/Event_Log_Workflow.pptx new file mode 100644 index 0000000..258fa31 Binary files /dev/null and b/doc/Event_Log_Workflow.pptx differ