diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java index 12b86bb..7ec4f2e 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java @@ -610,8 +610,8 @@ public abstract class SdDataSource { mSdData.alarmCause = ""; boolean flapDetected = flapCheck(); - alarmCheck(flapDetected); hrCheck(); + alarmCheck(flapDetected); o2SatCheck(); fallCheck(); muteCheck(); @@ -739,7 +739,10 @@ public abstract class SdDataSource { mAlarmCount += mSamplePeriod; if (mAlarmCount > mAlarmTime) { - if (mRequireHrForSeizureAlarm) { + if (mSdData.alarmState == 2) { + // Already in full alarm. Do not require HR confirmation again. + mSdData.alarmState = 2; + } else if (mRequireHrForSeizureAlarm) { if (hasRecentHrConfirmation()) { mSdData.alarmState = 2; mSdData.alarmCause = mSdData.alarmCause + "HR_CONFIRM "; @@ -753,9 +756,6 @@ public abstract class SdDataSource { // Existing behavior mSdData.alarmState = 2; } - } else if (mAlarmCount > mWarnTime) { - // warning - mSdData.alarmState = 1; } } else { // If we are not in an ALARM state, revert back to WARNING, otherwise diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index 4b1d6a6..07b7f7b 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -125,8 +125,19 @@ public class SdServer extends Service implements SdDataReceiver { private boolean mMp3Alarm = false; private boolean mPhoneAlarm = false; private boolean mSMSAlarm = false; - private boolean mFlogaEmergencyEscalation = true; + // Emergency-services escalation SMS. + // This is separate from the normal carer/contact SMS. + private boolean mEmergencySmsEnabled = false; + private boolean mEmergencySmsNotifyContacts = true; + private String mEmergencySmsNumber = ""; + private String mEmergencySmsMessage = + "Possible seizure emergency. The wearer may be having a tonic-clonic seizure and the alarm has remained active."; private int mEmergencySmsDelaySecs = 300; + private EmergencySmsTimer mEmergencySmsTimer = null; + private boolean mEmergencySmsSent = false; + private long mAlarmStartTime = 0; + private String mLastLocationUrl = ""; + private String mLastAlarmDateStr = ""; private String[] mSMSNumbers; private String mSMSMsgStr = "default SMS Message"; private String mSMSFalseAlarmMsgStr = "default SMS False Alarm Message"; @@ -657,7 +668,16 @@ public class SdServer extends Service implements SdDataReceiver { if ((sdData.alarmState == 2) || (sdData.alarmState == 5)) { sdData.alarmPhrase = "ALARM"; sdData.alarmStanding = true; - if (mAlarmStartTime == 0) mAlarmStartTime = System.currentTimeMillis(); + + if (mAlarmStartTime == 0) { + mAlarmStartTime = System.currentTimeMillis(); + + Time tnow = new Time(Time.getCurrentTimezone()); + tnow.setToNow(); + mLastAlarmDateStr = tnow.format("%H:%M:%S %d/%m/%Y"); + + startEmergencySmsTimer(); + } if (mLogAlarms) { Log.v(TAG, "***ALARM*** - Logging to SD Card"); //writeAlarmToSD(); @@ -678,12 +698,6 @@ public class SdServer extends Service implements SdDataReceiver { - mSMSTime.toMillis(false)) > 60000) { sendSMSAlarm(); - // Floga: escalate to emergency call if seizure longer than 5 minutes - long alarmDurationSecs = (System.currentTimeMillis() - mAlarmStartTime) / 1000; - if (alarmDurationSecs >= 300 && mFlogaEmergencyEscalation) { - sendPhoneAlarm(); - Log.v(TAG, "Floga: 5 minute threshold reached - calling emergency contacts"); - } mSMSTime = tnow; } else { mUtil.showToast(getString(R.string.SMSAlarmAlreadySentMsg)); @@ -1067,6 +1081,46 @@ public class SdServer extends Service implements SdDataReceiver { } } + private void startEmergencySmsTimer() { + if (!mEmergencySmsEnabled) { + Log.v(TAG, "startEmergencySmsTimer() - emergency SMS disabled"); + return; + } + + if (mEmergencySmsSent) { + Log.v(TAG, "startEmergencySmsTimer() - emergency SMS already sent"); + return; + } + + if (mEmergencySmsTimer != null) { + Log.v(TAG, "startEmergencySmsTimer() - timer already running"); + return; + } + + if (mEmergencySmsNumber == null || mEmergencySmsNumber.trim().length() == 0) { + Log.w(TAG, "startEmergencySmsTimer() - no emergency SMS number set"); + return; + } + + Log.i(TAG, "startEmergencySmsTimer() - starting emergency SMS timer for " + + mEmergencySmsDelaySecs + " seconds"); + + runOnUiThread(new Runnable() { + public void run() { + mEmergencySmsTimer = + new EmergencySmsTimer(mEmergencySmsDelaySecs * 1000L, 1000); + mEmergencySmsTimer.start(); + } + }); + } + + private void stopEmergencySmsTimer() { + if (mEmergencySmsTimer != null) { + Log.i(TAG, "stopEmergencySmsTimer() - cancelling emergency SMS timer"); + mEmergencySmsTimer.cancel(); + mEmergencySmsTimer = null; + } + } /* * Start the timer that will automatically re-set a latched alarm after a given period. @@ -1111,6 +1165,12 @@ public class SdServer extends Service implements SdDataReceiver { Log.i(TAG, "acceptAlarm()"); mSdData.alarmStanding = false; mSdData.fallAlarmStanding = false; + mAlarmStartTime = 0; + mLastAlarmDateStr = ""; + mEmergencySmsSent = false; + + stopEmergencySmsTimer(); + mSdDataSource.acceptAlarm(); stopLatchTimer(); } @@ -1315,7 +1375,24 @@ public class SdServer extends Service implements SdDataReceiver { mSMSAlarm = SP.getBoolean("SMSAlarm", false); Log.v(TAG, "updatePrefs() - mSMSAlarm = " + mSMSAlarm); mUtil.writeToSysLogFile("updatePrefs() - mSMSAlarm = " + mSMSAlarm); - mFlogaEmergencyEscalation = SP.getBoolean("FlogaEmergencyEscalation", true); + mEmergencySmsEnabled = SP.getBoolean("EmergencySmsEnabled", false); + mEmergencySmsNotifyContacts = SP.getBoolean("EmergencySmsNotifyContacts", true); + mEmergencySmsNumber = SP.getString("EmergencySmsNumber", ""); + mEmergencySmsMessage = SP.getString( + "EmergencySmsMessage", + "Possible seizure emergency. The wearer may be having a tonic-clonic seizure and the alarm has remained active." + ); + + try { + String emergencyDelayStr = SP.getString("EmergencySmsDelaySecs", "300"); + mEmergencySmsDelaySecs = Integer.parseInt(emergencyDelayStr); + } catch (Exception ex) { + mEmergencySmsDelaySecs = 300; + } + + Log.v(TAG, "updatePrefs() - mEmergencySmsEnabled = " + mEmergencySmsEnabled); + Log.v(TAG, "updatePrefs() - mEmergencySmsNumber = " + mEmergencySmsNumber); + Log.v(TAG, "updatePrefs() - mEmergencySmsDelaySecs = " + mEmergencySmsDelaySecs); mPhoneAlarm = SP.getBoolean("PhoneCallAlarm", false); Log.v(TAG, "updatePrefs() - mSMSAlarm = " + mSMSAlarm); mUtil.writeToSysLogFile("updatePrefs() - mSMSAlarm = " + mSMSAlarm); @@ -1509,6 +1586,7 @@ public class SdServer extends Service implements SdDataReceiver { + ";u=" + df.format(ll.getAccuracy()) + "'>here"; String googleUrl = "https://www.google.com/maps/place?q=" + ll.getLatitude() + "%2C" + ll.getLongitude(); + mLastLocationUrl = googleUrl; String shortUuidStr = mUuidStr.substring(mUuidStr.length() - 6); String messageStr = "Location update for seizure detected at " @@ -1532,7 +1610,69 @@ public class SdServer extends Service implements SdDataReceiver { } + private void sendSMSDirect(String phoneNo, String msgStr) { + String number = phoneNo == null ? "" : phoneNo.trim(); + if (number.length() == 0) { + Log.w(TAG, "sendSMSDirect() - empty phone number, skipping"); + return; + } + + Log.i(TAG, "sendSMSDirect() - Sending to " + number); + + try { + SmsManager sm = SmsManager.getDefault(); + sm.sendTextMessage(number, null, msgStr, null, null); + } catch (Exception e) { + Log.e(TAG, "sendSMSDirect - Failed to send SMS Message"); + mUtil.writeToSysLogFile("sendSMSDirect - Failed to send SMS Message"); + Log.e(TAG, e.toString()); + mUtil.showToast(getString(R.string.failed_to_send_sms)); + } + } + private void sendEmergencySms() { + String shortUuidStr = mUuidStr.substring(mUuidStr.length() - 6); + + String locationPart; + if (mLastLocationUrl != null && mLastLocationUrl.length() > 0) { + locationPart = " Location: " + mLastLocationUrl; + } else { + locationPart = " Location unavailable."; + } + + String alarmTimePart; + if (mLastAlarmDateStr != null && mLastAlarmDateStr.length() > 0) { + alarmTimePart = " Alarm time: " + mLastAlarmDateStr + "."; + } else { + alarmTimePart = ""; + } + + String emergencyMessage = mEmergencySmsMessage + + alarmTimePart + + " Alarm duration: " + + mEmergencySmsDelaySecs + + " seconds." + + locationPart + + " Device ID: " + + shortUuidStr; + + Log.i(TAG, "sendEmergencySms() - sending emergency SMS to " + mEmergencySmsNumber); + Log.i(TAG, "sendEmergencySms() - message: " + emergencyMessage); + + sendSMSDirect(mEmergencySmsNumber, emergencyMessage); + + if (mEmergencySmsNotifyContacts) { + String contactMessage = "Emergency services have been contacted for seizure alert" + + alarmTimePart + + locationPart + + " Device: " + + shortUuidStr; + + for (int i = 0; i < mSMSNumbers.length; i++) { + sendSMSDirect(mSMSNumbers[i], contactMessage); + } + } + } /* * Latch alarm in alarm state for a given period (mLatchAlarmPeriod seconds) after the alarm is raised. @@ -1619,7 +1759,37 @@ public class SdServer extends Service implements SdDataReceiver { } } + private class EmergencySmsTimer extends CountDownTimer { + public EmergencySmsTimer(long startTime, long interval) { + super(startTime, interval); + } + @Override + public void onFinish() { + Log.i(TAG, "EmergencySmsTimer.onFinish()"); + + mEmergencySmsTimer = null; + + if (mEmergencySmsSent) { + Log.i(TAG, "EmergencySmsTimer.onFinish() - already sent"); + return; + } + + if (mSdData == null || !mSdData.alarmStanding) { + Log.i(TAG, "EmergencySmsTimer.onFinish() - alarm no longer standing, not sending"); + return; + } + + sendEmergencySms(); + mEmergencySmsSent = true; + } + + @Override + public void onTick(long msRemaining) { + Log.v(TAG, "EmergencySmsTimer.onTick() - time remaining = " + + msRemaining / 1000 + " sec"); + } + } /** * Inhibit fault alarm initiation for a period to avoid spurious warning * beeps caused by short term network interruptions. diff --git a/app/src/main/res/xml/alarm_prefs.xml b/app/src/main/res/xml/alarm_prefs.xml index 006e2e3..eeea977 100644 --- a/app/src/main/res/xml/alarm_prefs.xml +++ b/app/src/main/res/xml/alarm_prefs.xml @@ -87,6 +87,41 @@ android:summary="@string/sms_false_alarm_message_summary" android:title="@string/sms_false_alarm_message_title" /> + + + + + + + + + + + + + +