Merge branch 'beta'
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
OpenSeizureDetector Android App - Change Log
|
||||
============================================
|
||||
V4.2.11 - Added setting to change the delay before SMS alert is sent (Issue #202)
|
||||
V4.2.11 - Updated permissions handling to support Android 14 (needed to publish on Play Store)
|
||||
- added a crude 'flap' detector into OSD Algorithm
|
||||
- Added setting to change the delay before SMS alert is sent (Issue #202)
|
||||
- Added 'Send False Alarm notification Menu Option (Issue #206)'
|
||||
- Reduced the frequency of checking if we have unvalided events on the data sharing server to reduce data usage (Issue #201).
|
||||
- Improvements to Data Sharing Screen (Issue #199)
|
||||
V4.2.10 - fixed (infrequent) crash when opening data sharing page (#195), and crash if log manager fails to start (#196)
|
||||
V4.2.9 - fixed crash when using Polish translation.
|
||||
V4.2.8 -
|
||||
|
||||
@@ -7,7 +7,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "uk.org.openseizuredetector"
|
||||
minSdkVersion 23 // Android 6
|
||||
targetSdkVersion 33 // Android 13 = 33
|
||||
targetSdkVersion 34 // Android 14 = 34
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:versionCode="145"
|
||||
android:versionCode="150"
|
||||
android:versionName="4.2.11">
|
||||
<!-- android:allowBackup="false" -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
@@ -12,6 +12,8 @@
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH" />
|
||||
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
@@ -80,6 +82,7 @@
|
||||
|
||||
<service
|
||||
android:name=".SdServer"
|
||||
android:foregroundServiceType="health"
|
||||
android:exported="false" />
|
||||
|
||||
<activity android:name=".LogManagerControlActivity" />
|
||||
|
||||
@@ -127,7 +127,7 @@ public class FragmentCommon extends FragmentOsdBaseClass {
|
||||
tv.setTextColor(warnTextColour);
|
||||
}
|
||||
if (mConnection.mSdServer.mSdData.alarmStanding) {
|
||||
tv.setText(R.string.Alarm);
|
||||
tv.setText(getString(R.string.Alarm) + "\n" + mConnection.mSdServer.mSdData.alarmCause);
|
||||
tv.setBackgroundColor(alarmColour);
|
||||
tv.setTextColor(alarmTextColour);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
||||
private UiTimer mUiTimer;
|
||||
private ArrayList<HashMap<String, String>> mEventsList;
|
||||
private ArrayList<HashMap<String, String>> mRemoteEventsList;
|
||||
private ArrayList<ArrayList<HashMap<String, String>>> mGroupedRemoteEventsList; // Each item is a list of event objects, similar to mRemoteEventsList
|
||||
private ArrayList<HashMap<String, String>> mSysLogList;
|
||||
private SdServiceConnection mConnection;
|
||||
private OsdUtil mUtil;
|
||||
@@ -281,6 +282,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
||||
Log.v(TAG, "getRemoteEvents - skipping warning or NDA record");
|
||||
}
|
||||
}
|
||||
createGroupedEventsList();
|
||||
Log.v(TAG, "getRemoteEvents() - set mRemoteEventsList(). Updating UI");
|
||||
updateUi();
|
||||
} catch (JSONException e) {
|
||||
@@ -293,6 +295,15 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void createGroupedEventsList() {
|
||||
/**
|
||||
* Reads the complete list of remote events mRemoteEventsList and creates a new list mGroupedRemoteEventsList
|
||||
* where each item is a list of events that comprise a group based on time (all events within a 3 minute period are grouped together).
|
||||
*/
|
||||
Log.i(TAG, "createGroupedEventsList()");
|
||||
mGroupedRemoteEventsList = new ArrayList<ArrayList<HashMap<String,String>>>();
|
||||
// FIXME - Make this do something!
|
||||
}
|
||||
|
||||
private void updateUi() {
|
||||
Log.i(TAG, "updateUi()");
|
||||
@@ -305,10 +316,10 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
||||
TextView tv1 = (TextView) findViewById(R.id.num_local_events_tv);
|
||||
tv1.setText(String.format("%d", eventCount));
|
||||
});
|
||||
mLm.getLocalDatapointsCount((Long datapointsCount) -> {
|
||||
TextView tv2 = (TextView) findViewById(R.id.num_local_datapoints_tv);
|
||||
tv2.setText(String.format("%d", datapointsCount));
|
||||
});
|
||||
//mLm.getLocalDatapointsCount((Long datapointsCount) -> {
|
||||
// TextView tv2 = (TextView) findViewById(R.id.num_local_datapoints_tv);
|
||||
// tv2.setText(String.format("%d", datapointsCount));
|
||||
//});
|
||||
TextView tv3 = (TextView) findViewById(R.id.nda_time_remaining_tv);
|
||||
tv3.setText(String.format("%.1f hrs", mLm.mNDATimeRemaining));
|
||||
Log.d(TAG, "mNDATimeRemaining = " + String.format("%.1f hrs", mLm.mNDATimeRemaining));
|
||||
|
||||
@@ -237,6 +237,12 @@ public class MainActivity2 extends AppCompatActivity {
|
||||
mConnection.mSdServer.sendSMSAlarm();
|
||||
}
|
||||
return true;
|
||||
case R.id.action_send_false_alarm_sms:
|
||||
Log.i(TAG, "action_send_false_alarm_sms");
|
||||
if (mConnection.mBound) {
|
||||
mConnection.mSdServer.sendFalseAlarmSMS();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.action_authenticate_api:
|
||||
Log.i(TAG, "action_autheticate_api");
|
||||
|
||||
@@ -116,6 +116,15 @@ public class OsdUtil {
|
||||
};
|
||||
public String[] BT_PERMISSIONS;
|
||||
|
||||
public final String[] ACTIVITY_PERMISSIONS_API34 = {
|
||||
Manifest.permission.FOREGROUND_SERVICE_HEALTH,
|
||||
Manifest.permission.ACTIVITY_RECOGNITION
|
||||
};
|
||||
|
||||
public final String[] ACTIVITY_PERMISSIONS_OLD = {};
|
||||
public String[] ACTIVITY_PERMISSIONS;
|
||||
|
||||
|
||||
public OsdUtil(Context context, Handler handler) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
@@ -780,6 +789,33 @@ public class OsdUtil {
|
||||
return allOk;
|
||||
}
|
||||
|
||||
public String[] getRequiredActivityPermissions() {
|
||||
// API 34 is Android 14 - see https://developer.android.com/develop/connectivity/bluetooth/bt-permissions
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
Log.d(TAG, "getRequiredActivityPermissions() - using new Activity Permissions");
|
||||
ACTIVITY_PERMISSIONS = ACTIVITY_PERMISSIONS_API34;
|
||||
} else {
|
||||
Log.d(TAG, "getRequiredActivityPermissions() - using old Activity Permissions");
|
||||
ACTIVITY_PERMISSIONS = ACTIVITY_PERMISSIONS_OLD;
|
||||
}
|
||||
return (ACTIVITY_PERMISSIONS);
|
||||
}
|
||||
public boolean areActivityPermissionsOk() {
|
||||
String[] activityPermissions = getRequiredActivityPermissions();
|
||||
boolean allOk = true;
|
||||
Log.d(TAG, "areActivityPermissions OK()");
|
||||
for (int i = 0; i < activityPermissions.length; i++) {
|
||||
if (ContextCompat.checkSelfPermission(mContext, activityPermissions[i])
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
Log.i(TAG, activityPermissions[i] + " Permission Not Granted");
|
||||
allOk = false;
|
||||
}
|
||||
}
|
||||
return allOk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public double parseToDouble(String userInput) {
|
||||
/**
|
||||
* Parse a string to a double value, taking localisation into account.
|
||||
|
||||
@@ -40,6 +40,7 @@ public class SdData implements Parcelable {
|
||||
|
||||
// Seizure Detection Algorithm Selection
|
||||
public boolean mOsdAlarmActive;
|
||||
public boolean mFlapAlarmActive;
|
||||
public boolean mCnnAlarmActive;
|
||||
|
||||
/* Analysis settings */
|
||||
|
||||
@@ -89,6 +89,12 @@ public abstract class SdDataSource {
|
||||
private short mAlarmTime;
|
||||
private short mAlarmThresh;
|
||||
private short mAlarmRatioThresh;
|
||||
|
||||
private short mFlapThresh;
|
||||
private short mFlapRatioThresh;
|
||||
private double mFlapFreqMin;
|
||||
private double mFlapFreqMax;
|
||||
|
||||
private boolean mFallActive;
|
||||
private short mFallThreshMin;
|
||||
private short mFallThreshMax;
|
||||
@@ -462,6 +468,22 @@ public abstract class SdDataSource {
|
||||
return mag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getBinNo - returns the FFT bin number corresponding to a frequency freqHz in Hz when the sample frequency is
|
||||
* sampleFreq Hz and the number of samples collected is nSamp.
|
||||
* @param freqHz : The frequency (in Hz) for which the bin number is required.
|
||||
* @param sampleFreq: Sample Frequency (Hz)
|
||||
* @param nSamp: number of samples
|
||||
* @return FFT bin Number
|
||||
*/
|
||||
int freq2FftBin(double freqHz, double sampleFreq, int nSamp) {
|
||||
int binNo = (int)(nSamp * freqHz / sampleFreq);
|
||||
return binNo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* doAnalysis() - analyse the data if the accelerometer data array mAccData
|
||||
* and populate the output data structure mSdData
|
||||
@@ -482,12 +504,12 @@ public abstract class SdDataSource {
|
||||
Log.v(TAG, "doAnalysis(): mSampleFreq=" + mSampleFreq + " mNSamp=" + mSdData.mNsamp + ": freqRes=" + freqRes);
|
||||
Log.v(TAG, "doAnalysis(): rawData=" + Arrays.toString(mSdData.rawData));
|
||||
// Set the frequency bounds for the analysis in fft output bin numbers.
|
||||
nMin = (int) (mAlarmFreqMin / freqRes);
|
||||
nMax = (int) (mAlarmFreqMax / freqRes);
|
||||
nMin = freq2FftBin(mAlarmFreqMin, mSampleFreq, mSdData.mNsamp);
|
||||
nMax = freq2FftBin(mAlarmFreqMax, mSampleFreq, mSdData.mNsamp);
|
||||
// Calculate the bin number of the cutoff frequency
|
||||
nFreqCutoff = freq2FftBin(mFreqCutoff, mSampleFreq, mSdData.mNsamp);
|
||||
Log.v(TAG, "doAnalysis(): mAlarmFreqMin=" + mAlarmFreqMin + ", nMin=" + nMin
|
||||
+ ", mAlarmFreqMax=" + mAlarmFreqMax + ", nMax=" + nMax);
|
||||
// Calculate the bin number of the cutoff frequency
|
||||
nFreqCutoff = (int) (mFreqCutoff / freqRes);
|
||||
Log.v(TAG, "mFreqCutoff = " + mFreqCutoff + ", nFreqCutoff=" + nFreqCutoff);
|
||||
|
||||
DoubleFFT_1D fftDo = new DoubleFFT_1D(mSdData.mNsamp);
|
||||
@@ -583,7 +605,9 @@ public abstract class SdDataSource {
|
||||
|
||||
// Check this data to see if it represents an alarm state.
|
||||
mSdData.alarmCause = "";
|
||||
alarmCheck();
|
||||
|
||||
boolean flapDetected = flapCheck();
|
||||
alarmCheck(flapDetected);
|
||||
hrCheck();
|
||||
o2SatCheck();
|
||||
fallCheck();
|
||||
@@ -593,6 +617,84 @@ public abstract class SdDataSource {
|
||||
mSdDataReceiver.onSdDataReceived(mSdData); // and tell SdServer we have received data.
|
||||
}
|
||||
|
||||
/**
|
||||
* flapCheck() - Performs the same analysis as the main OSD algorithm, but over a narrow
|
||||
* frequency band to detect a flapping arm movement.
|
||||
* returns True if in an alarm state, or false if ok.
|
||||
* FIXME - we should generalise the OSD algorithm to allow several ROIs and thresholds to be
|
||||
* specified, rather than doing this separately like this.
|
||||
*/
|
||||
protected boolean flapCheck() {
|
||||
boolean retVal;
|
||||
int nMin = 0;
|
||||
int nMax = 0;
|
||||
int nFreqCutoff = 0;
|
||||
double[] fft = null;
|
||||
double roiRatio;
|
||||
double roiPower;
|
||||
try {
|
||||
// FIXME - Use specified sampleFreq, not this hard coded one
|
||||
mSampleFreq = 25;
|
||||
double freqRes = 1.0 * mSampleFreq / mSdData.mNsamp;
|
||||
Log.v(TAG, "flapCheck(): mSampleFreq=" + mSampleFreq + " mNSamp=" + mSdData.mNsamp + ": freqRes=" + freqRes);
|
||||
Log.v(TAG, "flapCheck(): rawData=" + Arrays.toString(mSdData.rawData));
|
||||
// Set the frequency bounds for the analysis in fft output bin numbers.
|
||||
nMin = freq2FftBin(mFlapFreqMin, mSampleFreq, mSdData.mNsamp);
|
||||
nMax = freq2FftBin(mFlapFreqMax, mSampleFreq, mSdData.mNsamp);
|
||||
// Calculate the bin number of the cutoff frequency
|
||||
nFreqCutoff = freq2FftBin(mFreqCutoff, mSampleFreq, mSdData.mNsamp);
|
||||
Log.v(TAG, "flapCheck(): flapFreqMin=" + mFlapFreqMin + ", nMin=" + nMin
|
||||
+ ", flapFreqMax=" + mFlapFreqMax + ", nMax=" + nMax);
|
||||
Log.v(TAG, "mFreqCutoff = " + mFreqCutoff + ", nFreqCutoff=" + nFreqCutoff);
|
||||
|
||||
DoubleFFT_1D fftDo = new DoubleFFT_1D(mSdData.mNsamp);
|
||||
fft = new double[mSdData.mNsamp * 2];
|
||||
System.arraycopy(mSdData.rawData, 0, fft, 0, mSdData.mNsamp);
|
||||
fftDo.realForward(fft);
|
||||
|
||||
// Calculate the whole spectrum power (well a value equivalent to it that avoids square root calculations
|
||||
// and zero any readings that are above the frequency cutoff.
|
||||
double specPower = 0;
|
||||
for (int i = 1; i < mSdData.mNsamp / 2; i++) {
|
||||
if (i <= nFreqCutoff) {
|
||||
specPower = specPower + getMagnitude(fft, i);
|
||||
} else {
|
||||
fft[2 * i] = 0.;
|
||||
fft[2 * i + 1] = 0.;
|
||||
}
|
||||
}
|
||||
specPower = specPower / mSdData.mNsamp / 2;
|
||||
specPower = specPower / ACCEL_SCALE_FACTOR;
|
||||
|
||||
// Calculate the Region of Interest power and power ratio.
|
||||
roiPower = 0;
|
||||
for (int i = nMin; i < nMax; i++) {
|
||||
roiPower = roiPower + getMagnitude(fft, i);
|
||||
}
|
||||
roiPower = roiPower / (nMax - nMin);
|
||||
roiPower = roiPower / ACCEL_SCALE_FACTOR;
|
||||
|
||||
roiRatio = 10 * roiPower / specPower;
|
||||
|
||||
Log.d(TAG, "flapCheck() - roiPower="+roiPower+", roiRatio="+roiRatio);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "flapCheck - Exception during Analysis"+e.toString());
|
||||
roiRatio = 0;
|
||||
roiPower = 0;
|
||||
}
|
||||
|
||||
retVal = false;
|
||||
if (roiPower > mFlapThresh) {
|
||||
if (roiRatio > mFlapRatioThresh) {
|
||||
Log.i(TAG,"flapCheck() - *** flap detected ***");
|
||||
retVal = true;
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* checkAlarm() - checks the current accelerometer data and uses
|
||||
@@ -600,7 +702,7 @@ public abstract class SdDataSource {
|
||||
* state.
|
||||
* Sets mSdData.alarmState and mSdData.hrAlarmStanding
|
||||
*/
|
||||
private void alarmCheck() {
|
||||
private void alarmCheck(boolean flapDetected) {
|
||||
boolean inAlarm = false;
|
||||
// Avoid potential divide by zero issue
|
||||
if (mSdData.specPower == 0)
|
||||
@@ -615,6 +717,13 @@ public abstract class SdDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
if (mSdData.mFlapAlarmActive) {
|
||||
if (flapDetected) {
|
||||
inAlarm = true;
|
||||
mSdData.alarmCause = mSdData.alarmCause + "Flap ";
|
||||
}
|
||||
}
|
||||
|
||||
if (mSdData.mCnnAlarmActive) {
|
||||
if (mSdData.mPseizure > 0.5) {
|
||||
inAlarm = true;
|
||||
@@ -1144,6 +1253,30 @@ public abstract class SdDataSource {
|
||||
Log.v(TAG, "updatePrefs() OsdAlarmActive = " + mSdData.mOsdAlarmActive);
|
||||
mUtil.writeToSysLogFile("updatePrefs() OsdAlarmActive = " + mSdData.mOsdAlarmActive);
|
||||
|
||||
mSdData.mFlapAlarmActive = SP.getBoolean("FlapAlarmActive", true);
|
||||
Log.v(TAG, "updatePrefs() FlapAlarmActive = " + mSdData.mFlapAlarmActive);
|
||||
mUtil.writeToSysLogFile("updatePrefs() FlaplarmActive = " + mSdData.mFlapAlarmActive);
|
||||
|
||||
prefStr = SP.getString("FlapAlarmThresh", "SET_FROM_XML");
|
||||
mFlapThresh = (short) Integer.parseInt(prefStr);
|
||||
Log.v(TAG, "updatePrefs() FlapAlarmThresh = " + mFlapThresh);
|
||||
mUtil.writeToSysLogFile("updatePrefs() FlapThresh = " + mFlapThresh);
|
||||
|
||||
prefStr = SP.getString("FlapAlarmRatioThresh", "SET_FROM_XML");
|
||||
mFlapRatioThresh = (short) Integer.parseInt(prefStr);
|
||||
Log.v(TAG, "updatePrefs() FlapAlarmRatioThresh = " + mFlapRatioThresh);
|
||||
mUtil.writeToSysLogFile("updatePrefs() FlapAlarmRatioThresh = " + mFlapRatioThresh);
|
||||
|
||||
prefStr = SP.getString("FlapAlarmFreqMin", "SET_FROM_XML");
|
||||
mFlapFreqMin = (double) Double.parseDouble(prefStr);
|
||||
Log.v(TAG, "updatePrefs() FlapAlarmFreqMin = " + mFlapFreqMin);
|
||||
mUtil.writeToSysLogFile("updatePrefs() FlapAlarmFreqMin = " + mFlapFreqMin);
|
||||
|
||||
prefStr = SP.getString("FlapAlarmFreqMax", "SET_FROM_XML");
|
||||
mFlapFreqMax = (double) Double.parseDouble(prefStr);
|
||||
Log.v(TAG, "updatePrefs() FlapAlarmFreqMax = " + mFlapFreqMax);
|
||||
mUtil.writeToSysLogFile("updatePrefs() FlapAlarmFreqMax = " + mFlapFreqMax);
|
||||
|
||||
mSdData.mCnnAlarmActive = SP.getBoolean("CnnAlarmActive", false);
|
||||
Log.v(TAG, "updatePrefs() CnnAlarmActive = " + mSdData.mCnnAlarmActive);
|
||||
mUtil.writeToSysLogFile("updatePrefs() CnnAlarmActive = " + mSdData.mCnnAlarmActive);
|
||||
|
||||
@@ -100,7 +100,7 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
private int mCurrentNotificationAlarmLevel = -999;
|
||||
private SdWebServer webServer = null;
|
||||
private final static String TAG = "SdServer";
|
||||
private Timer dataLogTimer = null;
|
||||
//private Timer dataLogTimer = null;
|
||||
private CancelAudibleTimer mCancelAudibleTimer = null;
|
||||
private int mCancelAudiblePeriod = 10; // Cancel Audible Period in minutes
|
||||
private long mCancelAudibleTimeRemaining = 0;
|
||||
@@ -127,6 +127,7 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
private boolean mSMSAlarm = false;
|
||||
private String[] mSMSNumbers;
|
||||
private String mSMSMsgStr = "default SMS Message";
|
||||
private String mSMSFalseAlarmMsgStr = "default SMS False Alarm Message";
|
||||
public Time mSMSTime = null; // last time we sent an SMS Alarm (limited to one per minute)
|
||||
public SmsTimer mSmsTimer = null; // Timer to wait for specified time before sending an alert to give the user chance to cancel it.
|
||||
public int mSmsTimerSecs = 10; // Time delay in seconds before sending SMS alert.
|
||||
@@ -140,7 +141,7 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
public boolean mLogNDA = false;
|
||||
|
||||
private String mAuthToken = null;
|
||||
private long mEventsTimerPeriod = 60; // Number of seconds between checks to see if there are unvalidated remote events.
|
||||
private long mEventsTimerPeriod = 600; // Number of seconds between checks to see if there are unvalidated remote events.
|
||||
private long mEventDuration = 120; // event duration in seconds - uploads datapoints that cover this time range centred on the event time.
|
||||
public long mDataRetentionPeriod = 1; // Prunes the local db so it only retains data younger than this duration (in days)
|
||||
private long mRemoteLogPeriod = 6; // Period in seconds between uploads to the remote server.
|
||||
@@ -329,10 +330,11 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
|
||||
|
||||
// Start timer to log data regularly..
|
||||
/*
|
||||
if (dataLogTimer == null) {
|
||||
Log.v(TAG, "onStartCommand(): starting dataLog timer");
|
||||
mUtil.writeToSysLogFile("SdServer.onStartCommand() - starting dataLog timer");
|
||||
/*dataLogTimer = new Timer();
|
||||
dataLogTimer = new Timer();
|
||||
dataLogTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -340,11 +342,13 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
logData();
|
||||
}
|
||||
}, 0, 1000 * 60);
|
||||
*/
|
||||
|
||||
} else {
|
||||
Log.v(TAG, "onStartCommand(): dataLog timer already running.");
|
||||
mUtil.writeToSysLogFile("SdServer.onStartCommand() - dataLog timer already running???");
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
if (mLogDataRemote) {
|
||||
startEventsTimer();
|
||||
@@ -958,6 +962,48 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends SMS Alarms to the telephone numbers specified in mSMSNumbers[]
|
||||
* Attempts to find a better location, and sends a second SMS after location search
|
||||
* complete (onLocationReceived()).
|
||||
*/
|
||||
public void sendFalseAlarmSMS() {
|
||||
AlertDialog ad;
|
||||
if (mSMSAlarm) {
|
||||
if (!mCancelAudible) {
|
||||
Log.i(TAG, "sendFalseAlarmsSMS() - Sending to " + mSMSNumbers.length + " Numbers");
|
||||
mUtil.writeToSysLogFile("SdServer.sendFalseAlarmsSMS()");
|
||||
Time tnow = new Time(Time.getCurrentTimezone());
|
||||
tnow.setToNow();
|
||||
String dateStr = tnow.format("%H:%M:%S %d/%m/%Y");
|
||||
String shortUuidStr = mUuidStr.substring(mUuidStr.length() - 6);
|
||||
|
||||
// SmsManager sm = SmsManager.getDefault();
|
||||
for (int i = 0; i < mSMSNumbers.length; i++) {
|
||||
Log.i(TAG, "sendFalseAlarmsSMS() - Sending to " + mSMSNumbers[i]);
|
||||
//sendSMS(new String(mSMSNumbers[i]), mSMSFalseAlarmMsgStr + " - " + dateStr + " " + shortUuidStr);
|
||||
try {
|
||||
SmsManager sm = SmsManager.getDefault();
|
||||
sm.sendTextMessage(mSMSNumbers[i], null, mSMSFalseAlarmMsgStr + " - " + dateStr + " " + shortUuidStr,
|
||||
null, null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "sendFalseAlarmsSMS - Failed to send SMS Message");
|
||||
mUtil.showToast(getString(R.string.failed_to_send_sms));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.i(TAG, "sendFalseAlarmSMS() - Cancel Audible Active - not sending SMS");
|
||||
mUtil.showToast(getString(R.string.cancel_audible_not_sending_sms));
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "sendFalseAlarmSMS() - SMS Alarms Disabled - not doing anything!");
|
||||
mUtil.showToast(getString(R.string.sms_alarms_disabled));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* smsCanelClickListener - onClickListener for the SMS cancel dialog box. If the
|
||||
@@ -1261,11 +1307,14 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
mUtil.writeToSysLogFile("updatePrefs() - mSMSAlarm = " + mSMSAlarm);
|
||||
String SMSNumberStr = SP.getString("SMSNumbers", "");
|
||||
mSMSNumbers = SMSNumberStr.split(",");
|
||||
mSMSMsgStr = SP.getString("SMSMsg", "Seizure Detected!!!");
|
||||
Log.v(TAG, "updatePrefs() - SMSNumberStr = " + SMSNumberStr);
|
||||
mUtil.writeToSysLogFile("updatePrefs() - SMSNumberStr = " + SMSNumberStr);
|
||||
Log.v(TAG, "updatePrefs() - mSMSNumbers = " + mSMSNumbers);
|
||||
mUtil.writeToSysLogFile("updatePrefs() - mSMSNumbers = " + mSMSNumbers);
|
||||
mSMSMsgStr = SP.getString("SMSMsg", "Seizure Detected!!!");
|
||||
Log.v(TAG, "updatePrefs() - SMSMsgStr = " + mSMSMsgStr);
|
||||
mSMSFalseAlarmMsgStr = SP.getString("SMSFalseAlarmMsg", "False Alarm, Sorry!");
|
||||
Log.v(TAG, "updatePrefs() - SMSFalseAlarmMsgStr = " + mSMSFalseAlarmMsgStr);
|
||||
|
||||
String smsDelayPeriodStr = SP.getString("SMSDelayPeriod","10");
|
||||
mSmsTimerSecs = Integer.parseInt(smsDelayPeriodStr);
|
||||
|
||||
@@ -88,8 +88,10 @@ public class StartupActivity extends AppCompatActivity {
|
||||
private boolean mLocationPermissions2Requested;
|
||||
private boolean mSmsPermissionsRequested;
|
||||
private boolean mPermissionsRequested;
|
||||
private boolean mActivityPermissionsRequested = false;
|
||||
private boolean mBindInProgress = false;
|
||||
|
||||
|
||||
public final String[] REQUIRED_PERMISSIONS = {
|
||||
//Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WAKE_LOCK,
|
||||
@@ -328,14 +330,22 @@ public class StartupActivity extends AppCompatActivity {
|
||||
//pb.setProgressDrawable(getResources().getDrawable(R.drawable.start_server));
|
||||
requestBTPermissions();
|
||||
allOk = false;
|
||||
} else if (mBleDeviceAddr.equals("")) {
|
||||
Log.i(TAG,"BLE data source selected, but no device address specified - starting BLEScanActivity");
|
||||
} else if (mBleDeviceAddr.equals("")) {
|
||||
Log.i(TAG, "BLE data source selected, but no device address specified - starting BLEScanActivity");
|
||||
Intent i;
|
||||
i = new Intent(getApplicationContext(), BLEScanActivity.class);
|
||||
startActivity(i);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
} else if (!mUtil.areActivityPermissionsOk()) {
|
||||
Log.i(TAG, "Activity permissions NOT OK");
|
||||
tv.setText(getString(R.string.ActivityPermissionWarning));
|
||||
tv.setBackgroundColor(alarmColour);
|
||||
tv.setTextColor(alarmTextColour);
|
||||
requestActivityPermissions();
|
||||
allOk = false;
|
||||
|
||||
} else if (smsAlarmsActive && !areSMSPermissions1OK()) {
|
||||
Log.i(TAG, "SMS permissions NOT OK");
|
||||
tv.setText(getString(R.string.SmsPermissionWarning));
|
||||
@@ -699,6 +709,7 @@ public class StartupActivity extends AppCompatActivity {
|
||||
return allOk;
|
||||
}
|
||||
|
||||
|
||||
public boolean areSMSPermissions1OK() {
|
||||
boolean allOk = true;
|
||||
Log.v(TAG, "areSMSPermissions1 OK()");
|
||||
@@ -879,6 +890,33 @@ public class StartupActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
public void requestActivityPermissions() {
|
||||
if (mActivityPermissionsRequested) {
|
||||
Log.i(TAG, "requestActivityPermissions() - request already sent - not doing anything");
|
||||
} else {
|
||||
Log.i(TAG, "requestActivityPermissions() - requesting permissions");
|
||||
mActivityPermissionsRequested = true;
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
|
||||
this);
|
||||
alertDialogBuilder
|
||||
.setTitle(R.string.activity_permissions_required)
|
||||
.setMessage(R.string.activity_permissions_rationale)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(getString(R.string.okBtnTxt), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
Log.i(TAG, "requestActivityPermissions(): Launching ActivityCompat.requestPermissions()");
|
||||
ActivityCompat.requestPermissions(StartupActivity.this,
|
||||
mUtil.getRequiredActivityPermissions(),
|
||||
49);
|
||||
}
|
||||
})
|
||||
.create().show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
String permissions[], int[] grantResults) {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
android:text="000" />
|
||||
</LinearLayout>
|
||||
|
||||
<!--
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -49,6 +50,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="000" />
|
||||
</LinearLayout>
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
26
app/src/main/res/layout/activity_main_simple.xml
Normal file
26
app/src/main/res/layout/activity_main_simple.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/versionTv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_name" />
|
||||
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragment_common_container_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/fragment_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
||||
@@ -73,6 +73,11 @@
|
||||
android:icon="@drawable/stop_server"
|
||||
app:showAsAction="never|withText"
|
||||
android:title="@string/test_sms_alarm_notification" />
|
||||
<item
|
||||
android:id="@+id/action_send_false_alarm_sms"
|
||||
android:icon="@drawable/stop_server"
|
||||
app:showAsAction="never|withText"
|
||||
android:title="@string/send_false_alarm_sms_menu_text" />
|
||||
</group>
|
||||
<group android:id="@+id/grp5">
|
||||
<!--<item
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<string name="app_name">OpenSeizureDetector</string>
|
||||
<string name="changelog">
|
||||
"\n
|
||||
\nV4.2.11 - Reduced data usage of Data Sharing system, and added a 'flap detector' component to detect high amplitude, low frequency movement
|
||||
\nV4.2 - Added support for PineTime and BangleJS Watches using Bluetooth data source.
|
||||
\n - Added support for Version 2 of the Garmin watch app, which reduces battery drain
|
||||
\n - Added new, swipeable user interface to simplify the main screen.
|
||||
@@ -578,6 +579,13 @@
|
||||
<string name="restart_server">Restart Server</string>
|
||||
<string name="error_failed_to_create_local_event">ERROR: failed to create local event</string>
|
||||
<string name="error_failed_to_start_log_manager">ERROR: Failed to start Log Manager</string>
|
||||
<string name="ActivityPermissionWarning">Problem with Activity Detection Permissions</string>
|
||||
<string name="activity_permissions_required">Activity Detection Permission Required.</string>
|
||||
<string name="activity_permissions_rationale">Permission required to analyse your movements to detect seizure activity.</string>
|
||||
<string name="sms_delay_sec">SMS Delay (sec)</string>
|
||||
<string name="sms_delay_sec_desc">The number of seconds to delay between an alarm initiating and an SMS message being sent (default=10)</string>
|
||||
<string name="send_false_alarm_sms_menu_text">Send False Alarm SMS</string>
|
||||
<string name="DefaultSMSFalseAlarmMsgText">False Alarm, Sorry!</string>
|
||||
<string name="sms_false_alarm_message_summary">Text of \'False Alarm\' SMS message</string>
|
||||
<string name="sms_false_alarm_message_title">SMS False Alarm Message</string>
|
||||
</resources>
|
||||
|
||||
@@ -66,6 +66,11 @@
|
||||
android:key="SMSMsg"
|
||||
android:summary="@string/sms_message_summary"
|
||||
android:title="@string/sms_message_title" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/DefaultSMSFalseAlarmMsgText"
|
||||
android:key="SMSFalseAlarmMsg"
|
||||
android:summary="@string/sms_false_alarm_message_summary"
|
||||
android:title="@string/sms_false_alarm_message_title" />
|
||||
</PreferenceCategory>
|
||||
<!--
|
||||
<PreferenceCategory android:title="Phone Call Alarm Settings">
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
android:key="OsdAlarmActive"
|
||||
android:summary="@string/OsdAlarmEnabledSummary"
|
||||
android:title="@string/OsdAlarmEnabledTitle" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="FlapAlarmActive"
|
||||
android:summary="Enable the experimental alarm to detect arm 'flapping' motion"
|
||||
android:title="Enable Flap Alarm" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="CnnAlarmActive"
|
||||
@@ -37,6 +42,7 @@
|
||||
android:summary=""
|
||||
android:title="@string/fall_detect_active_title" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/SeizureDetectorSettingsTitle">
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
@@ -83,6 +89,37 @@
|
||||
android:title="@string/sample_period_title" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="Flap Alarm Settings">
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="FlapAlarmActive"
|
||||
android:summary="Enable the experimental alarm to detect arm 'flapping' motion"
|
||||
android:title="Enable Flap Alarm" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="5000"
|
||||
android:key="FlapAlarmThresh"
|
||||
android:summary="Flap Alarm Threshold"
|
||||
android:title="Flap Alarm Threshold" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="90"
|
||||
android:key="FlapAlarmRatioThresh"
|
||||
android:summary="Flap Alarm Ratio Threshold"
|
||||
android:title="Flap Alarm Ratio Threshold" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="2"
|
||||
android:key="FlapAlarmFreqMin"
|
||||
android:summary="@string/AlarmFreqMinSummary"
|
||||
android:title="@string/AlarmFreqMinTitle" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="4"
|
||||
android:key="FlapAlarmFreqMax"
|
||||
android:summary="@string/AlarmFreqMaxSummary"
|
||||
android:title="@string/AlarmFreqMaxTitle" />
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
|
||||
<PreferenceCategory android:title="@string/ml_sd_settings_title">
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
|
||||
@@ -57,7 +57,7 @@ public class SdAlgHrTest extends TestCase {
|
||||
mSdAlgHr.checkHr(60.);
|
||||
mSdAlgHr.checkHr(70.);
|
||||
mSdAlgHr.checkHr(80.);
|
||||
double hrAv = mSdAlgHr.getAverageHrVal();
|
||||
double hrAv = mSdAlgHr.getAverageHrAverage();
|
||||
assertEquals(hrAv, 70);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.1.2'
|
||||
classpath 'com.android.tools.build:gradle:8.4.1'
|
||||
classpath 'com.google.gms:google-services:4.3.15'
|
||||
}
|
||||
}
|
||||
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Sat Nov 21 06:41:40 GMT 2020
|
||||
#Thu Oct 10 20:04:34 BST 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
|
||||
|
||||
Binary file not shown.
BIN
releases/app-release-4.2.11b.apk
Normal file
BIN
releases/app-release-4.2.11b.apk
Normal file
Binary file not shown.
BIN
releases/app-release-4.2.11c.apk
Normal file
BIN
releases/app-release-4.2.11c.apk
Normal file
Binary file not shown.
BIN
releases/app-release-4.2.11d.apk
Normal file
BIN
releases/app-release-4.2.11d.apk
Normal file
Binary file not shown.
Reference in New Issue
Block a user