Merge branch 'beta'

This commit is contained in:
Graham Jones
2025-02-14 19:41:03 +00:00
24 changed files with 390 additions and 25 deletions

View File

@@ -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 -

View File

@@ -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
}

View File

@@ -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" />

View File

@@ -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);
}

View File

@@ -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));

View File

@@ -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");

View File

@@ -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.

View File

@@ -40,6 +40,7 @@ public class SdData implements Parcelable {
// Seizure Detection Algorithm Selection
public boolean mOsdAlarmActive;
public boolean mFlapAlarmActive;
public boolean mCnnAlarmActive;
/* Analysis settings */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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"

View 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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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">

View File

@@ -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"

View File

@@ -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);
}
}

View File

@@ -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'
}
}

View File

@@ -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.

Binary file not shown.

Binary file not shown.