From f5e5c7e093d31a5521210252b3962cc163be8d9e Mon Sep 17 00:00:00 2001 From: Graham Jones Date: Mon, 16 Nov 2015 23:03:20 +0000 Subject: [PATCH] Started v2.0 of android app - re-factored so the service uses a datasource to populate the SdData class - so that we can make different data sources in the future. --- .gitignore | 5 + Android_Pebble_SD.iml | 2 +- app/app.iml | 2 +- app/src/main/AndroidManifest.xml | 4 +- .../org/openseizuredetector/MainActivity.java | 60 +- .../uk/org/openseizuredetector/SdData.java | 2 +- .../org/openseizuredetector/SdDataSource.java | 74 + .../SdDataSourcePebble.java | 441 ++++++ .../uk/org/openseizuredetector/SdServer.java | 1343 +++++++---------- 9 files changed, 1046 insertions(+), 887 deletions(-) create mode 100644 .gitignore create mode 100644 app/src/main/java/uk/org/openseizuredetector/SdDataSource.java create mode 100644 app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb0b4c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build +.gradle +.idea +app/build + diff --git a/Android_Pebble_SD.iml b/Android_Pebble_SD.iml index 2e54f2b..f983017 100644 --- a/Android_Pebble_SD.iml +++ b/Android_Pebble_SD.iml @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/app/app.iml b/app/app.iml index 09b3522..da9f13b 100644 --- a/app/app.iml +++ b/app/app.iml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 05ee419..425d13c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="17" + android:versionName="2.0a"> diff --git a/app/src/main/java/uk/org/openseizuredetector/MainActivity.java b/app/src/main/java/uk/org/openseizuredetector/MainActivity.java index 68d9d4e..05b9d49 100644 --- a/app/src/main/java/uk/org/openseizuredetector/MainActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/MainActivity.java @@ -25,9 +25,7 @@ package uk.org.openseizuredetector; -import android.app.ActionBar; import android.app.Activity; -import android.app.IntentService; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.content.ComponentName; @@ -35,22 +33,15 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; -import android.preference.Preference; import android.content.ServiceConnection; import android.content.SharedPreferences; -import android.content.Context; import android.graphics.Color; -import android.media.AudioManager; -import android.media.ToneGenerator; import android.net.Uri; -import android.net.wifi.WifiManager; -import android.net.wifi.WifiInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; -import android.os.RemoteException; import android.preference.PreferenceManager; import android.util.Log; import android.view.Menu; @@ -58,7 +49,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; -import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Button; import java.lang.reflect.Field; @@ -69,28 +59,12 @@ import java.util.Enumeration; import java.util.Timer; import java.util.TimerTask; import org.apache.http.conn.util.InetAddressUtils; -import java.lang.CharSequence; -import android.util.AttributeSet; //MPAndroidChart import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.listener.OnChartGestureListener; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; - -import uk.org.openseizuredetector.SdServer; public class MainActivity extends Activity { @@ -466,31 +440,31 @@ public class MainActivity extends Activity try { if (mBound) { tv = (TextView) findViewById(R.id.alarmTv); - if ((mSdServer.sdData.alarmState==0) - && !mSdServer.sdData.alarmStanding - && !mSdServer.sdData.fallAlarmStanding) { + if ((mSdServer.mSdData.alarmState==0) + && !mSdServer.mSdData.alarmStanding + && !mSdServer.mSdData.fallAlarmStanding) { tv.setText("OK"); tv.setBackgroundColor(okColour); } - if ((mSdServer.sdData.alarmState==1) - && !mSdServer.sdData.alarmStanding - && !mSdServer.sdData.fallAlarmStanding) { + if ((mSdServer.mSdData.alarmState==1) + && !mSdServer.mSdData.alarmStanding + && !mSdServer.mSdData.fallAlarmStanding) { tv.setText("WARNING"); tv.setBackgroundColor(warnColour); } - if (mSdServer.sdData.alarmStanding) { + if (mSdServer.mSdData.alarmStanding) { tv.setText("**ALARM**"); tv.setBackgroundColor(alarmColour); } - if (mSdServer.sdData.fallAlarmStanding) { + if (mSdServer.mSdData.fallAlarmStanding) { tv.setText("**FALL**"); tv.setBackgroundColor(alarmColour); } tv = (TextView) findViewById(R.id.pebTimeTv); - tv.setText(mSdServer.mPebbleStatusTime.format("%H:%M:%S")); + tv.setText(mSdServer.mSdData.dataTime.format("%H:%M:%S")); // Pebble Connected Phrase tv = (TextView) findViewById(R.id.pebbleTv); - if (mSdServer.sdData.pebbleConnected) { + if (mSdServer.mSdData.pebbleConnected) { tv.setText("Pebble Watch Connected OK"); tv.setBackgroundColor(okColour); } else { @@ -498,7 +472,7 @@ public class MainActivity extends Activity tv.setBackgroundColor(alarmColour); } tv = (TextView) findViewById(R.id.appTv); - if (mSdServer.sdData.pebbleAppRunning) { + if (mSdServer.mSdData.pebbleAppRunning) { tv.setText("Pebble App OK"); tv.setBackgroundColor(okColour); } else { @@ -506,19 +480,19 @@ public class MainActivity extends Activity tv.setBackgroundColor(alarmColour); } tv = (TextView) findViewById(R.id.battTv); - tv.setText("Pebble Battery = "+String.valueOf(mSdServer.sdData.batteryPc)+"%"); - if (mSdServer.sdData.batteryPc<=20) + tv.setText("Pebble Battery = "+String.valueOf(mSdServer.mSdData.batteryPc)+"%"); + if (mSdServer.mSdData.batteryPc<=20) tv.setBackgroundColor(alarmColour); - if (mSdServer.sdData.batteryPc>20) + if (mSdServer.mSdData.batteryPc>20) tv.setBackgroundColor(warnColour); - if (mSdServer.sdData.batteryPc>=40) + if (mSdServer.mSdData.batteryPc>=40) tv.setBackgroundColor(okColour); tv = (TextView) findViewById(R.id.debugTv); String specStr = ""; for (int i=0;i<10;i++) specStr = specStr - + mSdServer.sdData.simpleSpec[i] + + mSdServer.mSdData.simpleSpec[i] + ", "; tv.setText("Spec = "+specStr); } @@ -544,7 +518,7 @@ public class MainActivity extends Activity ArrayList yVals = new ArrayList(); for (int i = 0; i < 10; i++) { if (mSdServer!=null) - yVals.add(new Entry(mSdServer.sdData.simpleSpec[i], i)); + yVals.add(new Entry(mSdServer.mSdData.simpleSpec[i], i)); else yVals.add(new Entry(i, i)); } diff --git a/app/src/main/java/uk/org/openseizuredetector/SdData.java b/app/src/main/java/uk/org/openseizuredetector/SdData.java index da00bf8..bc7e5a8 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdData.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdData.java @@ -125,7 +125,7 @@ public class SdData implements Parcelable { jsonObj.put("dataTimeStr","00000000T000000"); jsonObj.put("dataTime","00-00-00 00:00:00"); } - Log.v(TAG,"sdData.dataTime = "+dataTime); + Log.v(TAG,"mSdData.dataTime = "+dataTime); jsonObj.put("maxVal",maxVal); jsonObj.put("maxFreq",maxFreq); jsonObj.put("specPower",specPower); diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java new file mode 100644 index 0000000..86adaf6 --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java @@ -0,0 +1,74 @@ +/* + Android_Pebble_sd - Android alarm client for openseizuredetector.. + + See http://openseizuredetector.org for more information. + + Copyright Graham Jones, 2015. + + This file is part of pebble_sd. + + Android_Pebble_sd is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Android_Pebble_sd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Android_pebble_sd. If not, see . + +*/ +package uk.org.openseizuredetector; + +import android.content.Context; +import android.util.Log; + +interface SdDataReceiver { + public void onSdDataReceived(SdData sdData); + public void onSdDataFault(SdData sdData); +} + +/** + * Abstract class for a seizure detector data source. Subclasses include a pebble smart watch data source and a + * network data source. + */ +public abstract class SdDataSource { + public SdData mSdData; + protected Context mContext; + protected SdDataReceiver mSdDataReceiver; + private String TAG = "SdDataSource"; + + public SdDataSource(Context context, SdDataReceiver sdDataReceiver) { + Log.v(TAG, "SdDataSource() Constructor"); + mContext = context; + mSdDataReceiver = sdDataReceiver; + mSdData = new SdData(); + } + + /** + * Returns the SdData object stored by this class. + * @return + */ + public SdData getSdData() { + return mSdData; + } + + /** + * Start the datasource updating - initialises from sharedpreferences first to + * make sure any changes to preferences are taken into account. + */ + public void start() { + Log.v(TAG, "start()"); + } + + /** + * Stop the datasource from updating + */ + public void stop() { + Log.v(TAG, "stop()"); + } + +} diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java new file mode 100644 index 0000000..6c90f8e --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java @@ -0,0 +1,441 @@ +/* + Android_Pebble_sd - Android alarm client for openseizuredetector.. + + See http://openseizuredetector.org for more information. + + Copyright Graham Jones, 2015. + + This file is part of pebble_sd. + + Android_Pebble_sd is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Android_Pebble_sd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Android_pebble_sd. If not, see . + +*/ +package uk.org.openseizuredetector; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; +import android.preference.PreferenceManager; +import android.text.format.Time; +import android.util.Log; +import android.widget.Toast; + +import com.getpebble.android.kit.Constants; +import com.getpebble.android.kit.PebbleKit; +import com.getpebble.android.kit.util.PebbleDictionary; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; + + + +/** + * Abstract class for a seizure detector data source. Subclasses include a pebble smart watch data source and a + * network data source. + */ +public class SdDataSourcePebble extends SdDataSource { + private Timer mSettingsTimer; + private Timer mStatusTimer; + private Time mPebbleStatusTime; + private boolean mPebbleAppRunningCheck = false; + private int mAppRestartTimeout = 10; // Timeout before re-starting watch app (sec). + //private Looper mServiceLooper; + private int mFaultTimerPeriod = 30; // Fault Timer Period in sec + private PebbleKit.PebbleDataReceiver msgDataHandler = null; + + + private String TAG = "SdDataSourcePebble"; + + private UUID SD_UUID = UUID.fromString("03930f26-377a-4a3d-aa3e-f3b19e421c9d"); + private int NSAMP = 512; // Number of samples in fft input dataset. + + private int KEY_DATA_TYPE = 1; + private int KEY_ALARMSTATE = 2; + private int KEY_MAXVAL = 3; + private int KEY_MAXFREQ = 4; + private int KEY_SPECPOWER = 5; + private int KEY_SETTINGS = 6; + private int KEY_ALARM_FREQ_MIN = 7; + private int KEY_ALARM_FREQ_MAX = 8; + private int KEY_WARN_TIME = 9; + private int KEY_ALARM_TIME = 10; + private int KEY_ALARM_THRESH = 11; + private int KEY_POS_MIN = 12; // position of first data point in array + private int KEY_POS_MAX = 13; // position of last data point in array. + private int KEY_SPEC_DATA = 14; // Spectrum data + private int KEY_ROIPOWER = 15; + private int KEY_NMIN = 16; + private int KEY_NMAX = 17; + private int KEY_ALARM_RATIO_THRESH = 18; + private int KEY_BATTERY_PC = 19; + //private int KEY_SET_SETTINGS =20; // Phone is asking us to update watch app settings. + private int KEY_FALL_THRESH_MIN = 21; + private int KEY_FALL_THRESH_MAX = 22; + private int KEY_FALL_WINDOW = 23; + private int KEY_FALL_ACTIVE = 24; + + // Values of the KEY_DATA_TYPE entry in a message + private int DATA_TYPE_RESULTS = 1; // Analysis Results + private int DATA_TYPE_SETTINGS = 2; // Settings + private int DATA_TYPE_SPEC = 3; // FFT Spectrum (or part of a spectrum) + + public SdDataSourcePebble(Context context, SdDataReceiver sdDataReceiver) { + super(context,sdDataReceiver); + } + + + /** + * Start the datasource updating - initialises from sharedpreferences first to + * make sure any changes to preferences are taken into account. + */ + public void start() { + Log.v(TAG, "start()"); + updatePrefs(); + startPebbleServer(); + // Start timer to check status of pebble regularly. + mPebbleStatusTime = new Time(Time.getCurrentTimezone()); + //getPebbleStatus(); + if (mStatusTimer == null) { + Log.v(TAG, "onCreate(): starting status timer"); + mStatusTimer = new Timer(); + mStatusTimer.schedule(new TimerTask() { + @Override + public void run() { + getPebbleStatus(); + } + }, 0, 1000); + } else { + Log.v(TAG, "onCreate(): status timer already running."); + } + + // Start timer to retrieve pebble settings regularly. + getPebbleSdSettings(); + if (mSettingsTimer == null) { + Log.v(TAG, "onCreate(): starting settings timer"); + mSettingsTimer = new Timer(); + mSettingsTimer.schedule(new TimerTask() { + @Override + public void run() { + getPebbleSdSettings(); + } + }, 0, 1000 * 60); + } else { + Log.v(TAG, "onCreate(): settings timer already running."); + } + + + } + + /** + * Stop the datasource from updating + */ + public void stop() { + Log.v(TAG, "stop()"); + try { + // Stop the status timer + if (mStatusTimer != null) { + Log.v(TAG, "onDestroy(): cancelling status timer"); + mStatusTimer.cancel(); + mStatusTimer.purge(); + mStatusTimer = null; + } + // Stop the settings timer + if (mSettingsTimer != null) { + Log.v(TAG, "onDestroy(): cancelling settings timer"); + mSettingsTimer.cancel(); + mSettingsTimer.purge(); + mSettingsTimer = null; + } + // Stop pebble message handler. + Log.v(TAG, "onDestroy(): stopping pebble server"); + stopPebbleServer(); + + } catch (Exception e) { + Log.v(TAG, "Error in stop() - " + e.toString()); + } + + + } + + /** + * updatePrefs() - update basic settings from the SharedPreferences + * - defined in res/xml/SdDataSourcePebblePrefs.xml + */ + public void updatePrefs() { + Log.v(TAG, "updatePrefs()"); + SharedPreferences SP = PreferenceManager + .getDefaultSharedPreferences(mContext); + try { + // Parse the AppRestartTimeout period setting. + try { + String appRestartTimeoutStr = SP.getString("AppRestartTimeout", "10"); + mAppRestartTimeout = Integer.parseInt(appRestartTimeoutStr); + Log.v(TAG, "updatePrefs() - mAppRestartTimeout = " + mAppRestartTimeout); + } catch (Exception ex) { + Log.v(TAG, "updatePrefs() - Problem with AppRestartTimeout preference!"); + Toast toast = Toast.makeText(mContext, "Problem Parsing AppRestartTimeout Preference", Toast.LENGTH_SHORT); + toast.show(); + } + + + // Parse the FaultTimer period setting. + try { + String faultTimerPeriodStr = SP.getString("FaultTimerPeriod", "30"); + mFaultTimerPeriod = Integer.parseInt(faultTimerPeriodStr); + Log.v(TAG, "updatePrefs() - mFaultTimerPeriod = " + mFaultTimerPeriod); + } catch (Exception ex) { + Log.v(TAG, "updatePrefs() - Problem with FaultTimerPeriod preference!"); + Toast toast = Toast.makeText(mContext, "Problem Parsing FaultTimerPeriod Preference", Toast.LENGTH_SHORT); + toast.show(); + } + + + // Watch Settings + PebbleDictionary setDict = new PebbleDictionary(); + short intVal; + String prefStr; + prefStr = SP.getString("AlarmFreqMin", "5"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmFreqMin = " + intVal); + setDict.addInt16(KEY_ALARM_FREQ_MIN, intVal); + + prefStr = SP.getString("AlarmFreqMax", "10"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmFreqMax = " + intVal); + setDict.addUint16(KEY_ALARM_FREQ_MAX, (short) intVal); + + prefStr = SP.getString("WarnTime", "5"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() WarnTime = " + intVal); + setDict.addUint16(KEY_WARN_TIME, (short) intVal); + + prefStr = SP.getString("AlarmTime", "10"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmTime = " + intVal); + setDict.addUint16(KEY_ALARM_TIME, (short) intVal); + + prefStr = SP.getString("AlarmThresh", "100"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmThresh = " + intVal); + setDict.addUint16(KEY_ALARM_THRESH, (short) intVal); + + prefStr = SP.getString("AlarmRatioThresh", "30"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmRatioThresh = " + intVal); + setDict.addUint16(KEY_ALARM_RATIO_THRESH, (short) intVal); + + boolean fallActiveBool = SP.getBoolean("FallActive", false); + Log.v(TAG, "updatePrefs() FallActive = " + fallActiveBool); + if (fallActiveBool) + setDict.addUint16(KEY_FALL_ACTIVE, (short) 1); + else + setDict.addUint16(KEY_FALL_ACTIVE, (short) 0); + + prefStr = SP.getString("FallThreshMin", "200"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() FallThreshMin = " + intVal); + setDict.addUint16(KEY_FALL_THRESH_MIN, (short) intVal); + + prefStr = SP.getString("FallThreshMax", "1200"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() FallThreshMax = " + intVal); + setDict.addUint16(KEY_FALL_THRESH_MAX, (short) intVal); + + prefStr = SP.getString("FallWindow", "1500"); + intVal = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() FallWindow = " + intVal); + setDict.addUint16(KEY_FALL_WINDOW, (short) intVal); + + + // Send Watch Settings to Pebble + Log.v(TAG, "updatePrefs() - setDict = " + setDict.toJsonString()); + PebbleKit.sendDataToPebble(mContext, SD_UUID, setDict); + } catch (Exception ex) { + Log.v(TAG, "updatePrefs() - Problem parsing preferences!"); + Toast toast = Toast.makeText(mContext, "Problem Parsing Preferences - Something won't work - Please go back to Settings and correct it!", Toast.LENGTH_SHORT); + toast.show(); + } + } + + + /** + * Set this server to receive pebble data by registering it as + * A PebbleDataReceiver + */ + private void startPebbleServer() { + Log.v(TAG, "StartPebbleServer()"); + final Handler handler = new Handler(); + msgDataHandler = new PebbleKit.PebbleDataReceiver(SD_UUID) { + @Override + public void receiveData(final Context context, + final int transactionId, + final PebbleDictionary data) { + Log.v(TAG, "Received message from Pebble - data type=" + + data.getUnsignedIntegerAsLong(KEY_DATA_TYPE)); + // If we ha ve a message, the app must be running + mPebbleAppRunningCheck = true; + PebbleKit.sendAckToPebble(context, transactionId); + //Log.v(TAG,"Message is: "+data.toJsonString()); + if (data.getUnsignedIntegerAsLong(KEY_DATA_TYPE) + == DATA_TYPE_RESULTS) { + Log.v(TAG, "DATA_TYPE = Results"); + mSdData.dataTime.setToNow(); + Log.v(TAG, "mSdData.dataTime=" + mSdData.dataTime); + + mSdData.alarmState = data.getUnsignedIntegerAsLong( + KEY_ALARMSTATE); + mSdData.maxVal = data.getUnsignedIntegerAsLong(KEY_MAXVAL); + mSdData.maxFreq = data.getUnsignedIntegerAsLong(KEY_MAXFREQ); + mSdData.specPower = data.getUnsignedIntegerAsLong(KEY_SPECPOWER); + mSdData.roiPower = data.getUnsignedIntegerAsLong(KEY_ROIPOWER); + mSdData.alarmPhrase = "Unknown"; + mSdDataReceiver.onSdDataReceived(mSdData); + } + + + // Read the data that has been sent, and convert it into + // an integer array. + byte[] byteArr = data.getBytes(KEY_SPEC_DATA); + if ((byteArr!=null) && (byteArr.length!=0)) { + IntBuffer intBuf = ByteBuffer.wrap(byteArr) + .order(ByteOrder.LITTLE_ENDIAN) + .asIntBuffer(); + int[] intArray = new int[intBuf.remaining()]; + intBuf.get(intArray); + for (int i = 0; i < intArray.length; i++) { + mSdData.simpleSpec[i] = intArray[i]; + } + } else { + Log.v(TAG,"***** zero length spectrum received - error!!!!"); + } + + + if (data.getUnsignedIntegerAsLong(KEY_DATA_TYPE) + == DATA_TYPE_SETTINGS) { + Log.v(TAG, "DATA_TYPE = Settings"); + mSdData.alarmFreqMin = data.getUnsignedIntegerAsLong(KEY_ALARM_FREQ_MIN); + mSdData.alarmFreqMax = data.getUnsignedIntegerAsLong(KEY_ALARM_FREQ_MAX); + mSdData.nMin = data.getUnsignedIntegerAsLong(KEY_NMIN); + mSdData.nMax = data.getUnsignedIntegerAsLong(KEY_NMAX); + mSdData.warnTime = data.getUnsignedIntegerAsLong(KEY_WARN_TIME); + mSdData.alarmTime = data.getUnsignedIntegerAsLong(KEY_ALARM_TIME); + mSdData.alarmThresh = data.getUnsignedIntegerAsLong(KEY_ALARM_THRESH); + mSdData.alarmRatioThresh = data.getUnsignedIntegerAsLong(KEY_ALARM_RATIO_THRESH); + mSdData.batteryPc = data.getUnsignedIntegerAsLong(KEY_BATTERY_PC); + mSdData.haveSettings = true; + } + } + }; + PebbleKit.registerReceivedDataHandler(mContext, msgDataHandler); + } + + /** + * De-register this server from receiving pebble data + */ + public void stopPebbleServer() { + Log.v(TAG, "stopPebbleServer(): Stopping Pebble Server"); + Log.v(TAG, "stopPebbleServer(): msgDataHandler = " + msgDataHandler.toString()); + try { + mContext.unregisterReceiver(msgDataHandler); + } catch (Exception e) { + Log.v(TAG, "stopPebbleServer() - error " + e.toString()); + } + } + + /** + * Attempt to start the pebble_sd watch app on the pebble watch. + */ + public void startWatchApp() { + PebbleKit.startAppOnPebble(mContext, SD_UUID); + + } + + /** + * stop the pebble_sd watch app on the pebble watch. + */ + public void stopWatchApp() { + PebbleKit.closeAppOnPebble(mContext, SD_UUID); + } + + + /** + * Request Pebble App to send us its latest settings. + * Will be received as a message by the receiveData handler + */ + public void getPebbleSdSettings() { + Log.v(TAG, "getPebbleSdSettings() - requesting settings from pebble"); + PebbleDictionary data = new PebbleDictionary(); + data.addUint8(KEY_SETTINGS, (byte) 1); + PebbleKit.sendDataToPebble( + mContext, + SD_UUID, + data); + } + + /** + * Checks the status of the connection to the pebble watch, + * and sets class variables for use by other functions. + * If the watch app is not running, it attempts to re-start it. + */ + public void getPebbleStatus() { + Time tnow = new Time(Time.getCurrentTimezone()); + long tdiff; + tnow.setToNow(); + tdiff = (tnow.toMillis(false) - mPebbleStatusTime.toMillis(false)); + // Check we are actually connected to the pebble. + mSdData.pebbleConnected = PebbleKit.isWatchConnected(mContext); + // And is the pebble_sd app running? + // set mPebbleAppRunningCheck has been false for more than 10 seconds + // the app is not talking to us + // mPebbleAppRunningCheck is set to true in the receiveData handler. + if (!mPebbleAppRunningCheck && + (tdiff > mAppRestartTimeout * 1000)) { + Log.v(TAG, "getPebbleStatus() - tdiff = " + tdiff); + mSdData.pebbleAppRunning = false; + Log.v(TAG, "getPebbleStatus() - Pebble App Not Running - Attempting to Re-Start"); + startWatchApp(); + getPebbleSdSettings(); + // Only make audible warning beep if we have not received data for more than mFaultTimerPeriod seconds. + if (tdiff > mFaultTimerPeriod * 1000) { + mSdDataReceiver.onSdDataFault(mSdData); + } else { + Log.v(TAG, "getPebbleStatus() - Waiting for mFaultTimerPeriod before issuing audible warning..."); + } + } else { + mSdData.pebbleAppRunning = true; + } + + // if we have confirmation that the app is running, reset the + // status time to now and initiate another check. + if (mPebbleAppRunningCheck) { + mPebbleAppRunningCheck = false; + mPebbleStatusTime.setToNow(); + } + + if (!mSdData.haveSettings) { + Log.v(TAG, "getPebbleStatus() - no settings received yet - requesting"); + getPebbleSdSettings(); + } + } + + + + +} + diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index bf3da71..60b5c76 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -27,11 +27,9 @@ package uk.org.openseizuredetector; import java.util.Map; + import fi.iki.elonen.NanoHTTPD; -import android.app.Activity; -import android.app.ActivityManager; -import android.app.ActivityManager.RunningServiceInfo; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -39,89 +37,40 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; -import android.content.res.AssetFileDescriptor; import android.content.SharedPreferences; import android.media.AudioManager; import android.media.ToneGenerator; -import android.os.Bundle; import android.os.Environment; -import android.os.Handler; import android.os.HandlerThread; import android.os.Binder; -import android.os.CountDownTimer; import android.os.IBinder; -import android.os.Looper; -import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; -import android.os.Process; import android.preference.PreferenceManager; import android.telephony.SmsManager; -import android.telephony.SmsMessage; import android.util.Log; import android.widget.Toast; + import java.util.Timer; import java.util.TimerTask; import java.io.*; import java.util.*; -import java.util.UUID; import java.util.StringTokenizer; -import java.net.URL; -import android.net.Uri; -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; -import java.nio.IntBuffer; -import java.nio.ByteOrder; + import android.text.format.Time; + import org.json.JSONObject; import org.json.JSONArray; -import com.getpebble.android.kit.Constants; -import com.getpebble.android.kit.PebbleKit; -import com.getpebble.android.kit.util.PebbleDictionary; /** * Based on example at: * http://stackoverflow.com/questions/14309256/using-nanohttpd-in-android - * and + * and * http://developer.android.com/guide/components/services.html#ExtendingService */ -public class SdServer extends Service -{ - private UUID SD_UUID = UUID.fromString("03930f26-377a-4a3d-aa3e-f3b19e421c9d"); - private int NSAMP = 512; // Number of samples in fft input dataset. - - private int KEY_DATA_TYPE = 1; - private int KEY_ALARMSTATE = 2; - private int KEY_MAXVAL = 3; - private int KEY_MAXFREQ = 4; - private int KEY_SPECPOWER = 5; - private int KEY_SETTINGS = 6; - private int KEY_ALARM_FREQ_MIN =7; - private int KEY_ALARM_FREQ_MAX =8; - private int KEY_WARN_TIME = 9; - private int KEY_ALARM_TIME = 10; - private int KEY_ALARM_THRESH = 11; - private int KEY_POS_MIN = 12; // position of first data point in array - private int KEY_POS_MAX = 13; // position of last data point in array. - private int KEY_SPEC_DATA = 14; // Spectrum data - private int KEY_ROIPOWER = 15; - private int KEY_NMIN = 16; - private int KEY_NMAX = 17; - private int KEY_ALARM_RATIO_THRESH = 18; - private int KEY_BATTERY_PC = 19; - //private int KEY_SET_SETTINGS =20; // Phone is asking us to update watch app settings. - private int KEY_FALL_THRESH_MIN = 21; - private int KEY_FALL_THRESH_MAX = 22; - private int KEY_FALL_WINDOW = 23; - private int KEY_FALL_ACTIVE =24; - - // Values of the KEY_DATA_TYPE entry in a message - private int DATA_TYPE_RESULTS = 1; // Analysis Results - private int DATA_TYPE_SETTINGS = 2; // Settings - private int DATA_TYPE_SPEC = 3; // FFT Spectrum (or part of a spectrum) - +public class SdServer extends Service implements SdDataReceiver { // Notification ID private int NOTIFICATION_ID = 1; @@ -129,18 +78,11 @@ public class SdServer extends Service private WebServer webServer = null; private final static String TAG = "SdServer"; - private Looper mServiceLooper; - public Time mPebbleStatusTime; - private boolean mPebbleAppRunningCheck = false; - private Timer statusTimer = null; - private int mFaultTimerPeriod = 30; // Fault Timer Period in sec - private int mAppRestartTimeout = 10; // Timeout before re-starting watch app (sec). - private Timer settingsTimer = null; private Timer dataLogTimer = null; private HandlerThread thread; private WakeLock mWakeLock = null; - public SdData sdData; - private PebbleKit.PebbleDataReceiver msgDataHandler = null; + public SdDataSource mSdDataSource; + public SdData mSdData; private boolean mLatchAlarms = false; private boolean mCancelAudible = false; private boolean mAudibleAlarm = false; @@ -158,44 +100,44 @@ public class SdServer extends Service /** * class to handle binding the MainApp activity to this service - * so it can access sdData. + * so it can access mSdData. */ public class SdBinder extends Binder { - SdServer getService() { - return SdServer.this; - } + SdServer getService() { + return SdServer.this; + } } /** * Constructor for SdServer class - does not do much! */ public SdServer() { - super(); - sdData = new SdData(); - Log.v(TAG,"SdServer Created"); + super(); + mSdData = new SdData(); + Log.v(TAG, "SdServer Created"); } @Override public IBinder onBind(Intent intent) { - Log.v(TAG,"sdServer.onBind()"); - return mBinder; + Log.v(TAG, "sdServer.onBind()"); + return mBinder; } - /** * onCreate() - called when services is created. Starts message * handler process to listen for messages from other processes. */ @Override public void onCreate() { - Log.v(TAG,"onCreate()"); + Log.v(TAG, "onCreate()"); - // Create a wake lock, but don't use it until the service is started. - PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - "MyWakelockTag"); + + // Create a wake lock, but don't use it until the service is started. + PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "MyWakelockTag"); } /** @@ -204,204 +146,256 @@ public class SdServer extends Service */ @Override public int onStartCommand(Intent intent, int flags, int startId) { - Log.v(TAG,"onStartCommand() - SdServer service starting"); - - // Update preferences. - Log.v(TAG,"onStartCommand() - calling updatePrefs()"); - updatePrefs(); - - // Display a notification icon in the status bar of the phone to - // show the service is running. - Log.v(TAG,"showing Notification"); - showNotification(); + Log.v(TAG, "onStartCommand() - SdServer service starting"); - // Record last time we sent an SMS so we can limit rate of SMS - // sending to one per minute. - mSMSTime = new Time(Time.getCurrentTimezone()); + // Update preferences. + Log.v(TAG, "onStartCommand() - calling updatePrefs()"); + updatePrefs(); + mSdDataSource = new SdDataSourcePebble(this.getApplicationContext(), this); + mSdDataSource.start(); - // Start receiving data from the pebble watch - startPebbleServer(); + // Display a notification icon in the status bar of the phone to + // show the service is running. + Log.v(TAG, "showing Notification"); + showNotification(); - // Start timer to check status of pebble regularly. - mPebbleStatusTime = new Time(Time.getCurrentTimezone()); - //getPebbleStatus(); - if (statusTimer==null) { - Log.v(TAG,"onCreate(): starting status timer"); - statusTimer = new Timer(); - statusTimer.schedule(new TimerTask() { - @Override - public void run() {getPebbleStatus();} - }, 0, 1000); - } else { - Log.v(TAG,"onCreate(): status timer already running."); - } - - // Start timer to retrieve pebble settings regularly. - getPebbleSdSettings(); - if (settingsTimer == null) { - Log.v(TAG,"onCreate(): starting settings timer"); - settingsTimer = new Timer(); - settingsTimer.schedule(new TimerTask() { - @Override - public void run() {getPebbleSdSettings();} - }, 0, 1000*60); - } else { - Log.v(TAG,"onCreate(): settings timer already running."); - } - - // Start timer to log data regularly.. - if (dataLogTimer == null) { - Log.v(TAG,"onCreate(): starting dataLog timer"); - dataLogTimer = new Timer(); - dataLogTimer.schedule(new TimerTask() { - @Override - public void run() {logData();} - }, 0, 1000*60); - } else { - Log.v(TAG,"onCreate(): dataLog timer already running."); - } + // Record last time we sent an SMS so we can limit rate of SMS + // sending to one per minute. + mSMSTime = new Time(Time.getCurrentTimezone()); - // Start the web server - startWebServer(); - // Apply the wake-lock to prevent CPU sleeping (very battery intensive!) - if (mWakeLock!=null) { - mWakeLock.acquire(); - Log.v(TAG,"Applied Wake Lock to prevent device sleeping"); - } else { - Log.d(TAG,"mmm...mWakeLock is null, so not aquiring lock. This shouldn't happen!"); - } + // Start timer to log data regularly.. + if (dataLogTimer == null) { + Log.v(TAG, "onCreate(): starting dataLog timer"); + dataLogTimer = new Timer(); + dataLogTimer.schedule(new TimerTask() { + @Override + public void run() { + logData(); + } + }, 0, 1000 * 60); + } else { + Log.v(TAG, "onCreate(): dataLog timer already running."); + } - return START_STICKY; + + // Start the web server + startWebServer(); + + // Apply the wake-lock to prevent CPU sleeping (very battery intensive!) + if (mWakeLock != null) { + mWakeLock.acquire(); + Log.v(TAG, "Applied Wake Lock to prevent device sleeping"); + } else { + Log.d(TAG, "mmm...mWakeLock is null, so not aquiring lock. This shouldn't happen!"); + } + + return START_STICKY; } @Override public void onDestroy() { - Log.v(TAG,"onDestroy(): SdServer Service stopping"); - // release the wake lock to allow CPU to sleep and reduce - // battery drain. - if (mWakeLock!=null) { - mWakeLock.release(); - Log.v(TAG,"Released Wake Lock to allow device to sleep."); - } else { - Log.d(TAG, "mmm...mWakeLock is null, so not releasing lock. This shouldn't happen!"); - } + Log.v(TAG, "onDestroy(): SdServer Service stopping"); + // release the wake lock to allow CPU to sleep and reduce + // battery drain. + if (mWakeLock != null) { + mWakeLock.release(); + Log.v(TAG, "Released Wake Lock to allow device to sleep."); + } else { + Log.d(TAG, "mmm...mWakeLock is null, so not releasing lock. This shouldn't happen!"); + } - try { - // Stop the status timer - if (statusTimer!=null) { - Log.v(TAG,"onDestroy(): cancelling status timer"); - statusTimer.cancel(); - statusTimer.purge(); - statusTimer = null; - } - // Stop the settings timer - if (settingsTimer!=null) { - Log.v(TAG,"onDestroy(): cancelling settings timer"); - settingsTimer.cancel(); - settingsTimer.purge(); - settingsTimer = null; - } - // Cancel the notification. - Log.v(TAG,"onDestroy(): cancelling notification"); - mNM.cancel(NOTIFICATION_ID); - // Stop web server - Log.v(TAG,"onDestroy(): stopping web server"); - stopWebServer(); - // Stop pebble message handler. - Log.v(TAG,"onDestroy(): stopping pebble server"); - stopPebbleServer(); - // stop this service. - Log.v(TAG,"onDestroy(): calling stopSelf()"); - stopSelf(); + mSdDataSource.stop(); - } catch(Exception e) { - Log.v(TAG,"Error in onDestroy() - "+e.toString()); - } + try { + // Cancel the notification. + Log.v(TAG, "onDestroy(): cancelling notification"); + mNM.cancel(NOTIFICATION_ID); + // Stop web server + Log.v(TAG, "onDestroy(): stopping web server"); + stopWebServer(); + // stop this service. + Log.v(TAG, "onDestroy(): calling stopSelf()"); + stopSelf(); + + } catch (Exception e) { + Log.v(TAG, "Error in onDestroy() - " + e.toString()); + } } - /** * Show a notification while this service is running. */ private void showNotification() { - Log.v(TAG,"showNotification()"); + Log.v(TAG, "showNotification()"); CharSequence text = "OpenSeizureDetector Server Running"; - Notification notification = - new Notification(R.drawable.star_of_life_24x24, text, - System.currentTimeMillis()); - PendingIntent contentIntent = PendingIntent.getActivity(this, 0, + Notification notification = + new Notification(R.drawable.star_of_life_24x24, text, + System.currentTimeMillis()); + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0); notification.setLatestEventInfo(this, "OpenSeizureDetector Server", - text, contentIntent); - notification.flags |= Notification.FLAG_NO_CLEAR; - mNM = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); + text, contentIntent); + notification.flags |= Notification.FLAG_NO_CLEAR; + mNM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNM.notify(NOTIFICATION_ID, notification); } + /** + * Process the data received from the SdData source. + * @param sdData + */ + public void onSdDataReceived(SdData sdData) { + Log.v(TAG, "onSdDataReceived()"); + if (sdData.alarmState == 0) { + if ((!mLatchAlarms) || + (mLatchAlarms && + (!sdData.alarmStanding && !sdData.fallAlarmStanding))) { + sdData.alarmPhrase = "OK"; + sdData.alarmStanding = false; + sdData.fallAlarmStanding = false; + } + } + if (sdData.alarmState == 1) { + if ((!mLatchAlarms) || + (mLatchAlarms && + (!sdData.alarmStanding && !sdData.fallAlarmStanding))) { + sdData.alarmPhrase = "WARNING"; + sdData.alarmStanding = false; + sdData.fallAlarmStanding = false; + } + if (mLogAlarms) { + Log.v(TAG, "WARNING - Logging to SD Card"); + writeAlarmToSD(); + logData(); + } else { + Log.v(TAG, "WARNING"); + } + warningBeep(); + } + if ((sdData.alarmState == 2) || (sdData.alarmStanding)) { + sdData.alarmPhrase = "ALARM"; + sdData.alarmStanding = true; + if (mLogAlarms) { + Log.v(TAG, "***ALARM*** - Logging to SD Card"); + writeAlarmToSD(); + logData(); + } else { + Log.v(TAG, "***ALARM***"); + } + // Make alarm beep tone + alarmBeep(); + // Send SMS Alarm. + if (mSMSAlarm) { + Time tnow = new Time(Time.getCurrentTimezone()); + tnow.setToNow(); + // limit SMS alarms to one per minute + if ((tnow.toMillis(false) + - mSMSTime.toMillis(false)) + > 60000) { + sendSMSAlarm(); + mSMSTime = tnow; + } + } + } + if ((sdData.alarmState == 3) || (sdData.fallAlarmStanding)) { + sdData.alarmPhrase = "FALL"; + sdData.fallAlarmStanding = true; + if (mLogAlarms) { + Log.v(TAG, "***FALL*** - Logging to SD Card"); + writeAlarmToSD(); + logData(); + } else { + Log.v(TAG, "***FALL***"); + } + // Make alarm beep tone + alarmBeep(); + // Send SMS Alarm. + if (mSMSAlarm) { + Time tnow = new Time(Time.getCurrentTimezone()); + tnow.setToNow(); + // limit SMS alarms to one per minute + if ((tnow.toMillis(false) + - mSMSTime.toMillis(false)) + > 60000) { + sendSMSAlarm(); + mSMSTime = tnow; + } + } + + } + mSdData = sdData; + } + + public void onSdDataFault(SdData sdData) { + Log.v(TAG,"onSdDataFault()"); + if (mAudibleFaultWarning) { + faultWarningBeep(); + } + } /* from http://stackoverflow.com/questions/12154940/how-to-make-a-beep-in-android */ + /** * beep for duration miliseconds, but only if mAudibleAlarm is set. */ private void beep(int duration) { - ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 100); - toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, duration); - Log.v(TAG,"beep()"); + ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 100); + toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, duration); + Log.v(TAG, "beep()"); } /* * beep, provided mAudibleAlarm is set */ - public void faultWarningBeep() { - if (mCancelAudible) { - Log.v(TAG, "faultWarningBeep() - CancelAudible Active - silent beep..."); - } else { - if (mAudibleFaultWarning) { - beep(10); - Log.v(TAG, "faultWarningBeep()"); - } else { - Log.v(TAG, "faultWarningBeep() - silent..."); - } - } - } - + public void faultWarningBeep() { + if (mCancelAudible) { + Log.v(TAG, "faultWarningBeep() - CancelAudible Active - silent beep..."); + } else { + if (mAudibleFaultWarning) { + beep(10); + Log.v(TAG, "faultWarningBeep()"); + } else { + Log.v(TAG, "faultWarningBeep() - silent..."); + } + } + } /* * beep, provided mAudibleAlarm is set */ public void alarmBeep() { - if (mCancelAudible) { - Log.v(TAG,"alarmBeep() - CancelAudible Active - silent beep..."); - } else { - if (mAudibleAlarm) { - beep(1000); - Log.v(TAG,"alarmBeep()"); - } else { - Log.v(TAG,"alarmBeep() - silent..."); - } - } + if (mCancelAudible) { + Log.v(TAG, "alarmBeep() - CancelAudible Active - silent beep..."); + } else { + if (mAudibleAlarm) { + beep(1000); + Log.v(TAG, "alarmBeep()"); + } else { + Log.v(TAG, "alarmBeep() - silent..."); + } + } } /* * beep, provided mAudibleWarning is set */ public void warningBeep() { - if (mCancelAudible) { - Log.v(TAG,"warningBeep() - CancelAudible Active - silent beep..."); - } else { - if (mAudibleWarning) { - beep(100); - Log.v(TAG,"warningBeep()"); - } else { - Log.v(TAG,"warningBeep() - silent..."); - } - } + if (mCancelAudible) { + Log.v(TAG, "warningBeep() - CancelAudible Active - silent beep..."); + } else { + if (mAudibleWarning) { + beep(100); + Log.v(TAG, "warningBeep()"); + } else { + Log.v(TAG, "warningBeep() - silent..."); + } + } } @@ -409,203 +403,33 @@ public class SdServer extends Service * Sends SMS Alarms to the telephone numbers specified in mSMSNumbers[] */ public void sendSMSAlarm() { - if (mSMSAlarm) { - Log.v(TAG,"sendSMSAlarm() - Sending to "+mSMSNumbers.length+" Numbers"); - Time tnow = new Time(Time.getCurrentTimezone()); - tnow.setToNow(); - String dateStr = tnow.format("%Y-%m-%d %H-%M-%S"); - SmsManager sm = SmsManager.getDefault(); - for (int i=0;i 60000) { - sendSMSAlarm(); - mSMSTime = tnow; - } - } - } - if ((sdData.alarmState == 3) || (sdData.fallAlarmStanding)) { - sdData.alarmPhrase = "FALL"; - sdData.fallAlarmStanding = true; - if (mLogAlarms) { - Log.v(TAG, "***FALL*** - Logging to SD Card"); - writeAlarmToSD(); - logData(); - } else { - Log.v(TAG, "***FALL***"); - } - // Make alarm beep tone - alarmBeep(); - // Send SMS Alarm. - if (mSMSAlarm) { - Time tnow = new Time(Time.getCurrentTimezone()); - tnow.setToNow(); - // limit SMS alarms to one per minute - if ((tnow.toMillis(false) - - mSMSTime.toMillis(false)) - > 60000) { - sendSMSAlarm(); - mSMSTime = tnow; - } - } - } - - - // Read the data that has been sent, and convert it into - // an integer array. - byte[] byteArr = data.getBytes(KEY_SPEC_DATA); - IntBuffer intBuf = ByteBuffer.wrap(byteArr) - .order(ByteOrder.LITTLE_ENDIAN) - .asIntBuffer(); - int[] intArray = new int[intBuf.remaining()]; - intBuf.get(intArray); - for (int i=0;i mAppRestartTimeout * 1000)) { - Log.v(TAG, "getPebbleStatus() - tdiff = " + tdiff); - sdData.pebbleAppRunning = false; - Log.v(TAG, "getPebbleStatus() - Pebble App Not Running - Attempting to Re-Start"); - startWatchApp(); - getPebbleSdSettings(); - // Only make audible warning beep if we have not received data for more than mFaultTimerPeriod seconds. - if ((tdiff > mFaultTimerPeriod * 1000) - && (mAudibleFaultWarning) - ) { - faultWarningBeep(); - } else { - Log.v(TAG, "getPebbleStatus() - Waiting for mFaultTimerPeriod before issuing audible warning..."); - } - } else { - sdData.pebbleAppRunning = true; - } - - // if we have confirmation that the app is running, reset the - // status time to now and initiate another check. - if (mPebbleAppRunningCheck) { - mPebbleAppRunningCheck = false; - mPebbleStatusTime.setToNow(); - } - - if (!sdData.haveSettings) { - Log.v(TAG, "getPebbleStatus() - no settings received yet - requesting"); - getPebbleSdSettings(); - } - } - - /** - * Request Pebble App to send us its latest settings. - * Will be received as a message by the receiveData handler - */ - public void getPebbleSdSettings() { - Log.v(TAG,"getPebbleSdSettings() - requesting settings from pebble"); - PebbleDictionary data = new PebbleDictionary(); - data.addUint8(KEY_SETTINGS, (byte)1); - PebbleKit.sendDataToPebble( - getApplicationContext(), - SD_UUID, - data); + if (mLogData) { + Log.v(TAG, "logData() - writing data to SD Card"); + writeToSD(); + } } @@ -721,190 +483,106 @@ public class SdServer extends Service * - defined in res/xml/prefs.xml */ public void updatePrefs() { - Log.v(TAG,"updatePrefs()"); - SharedPreferences SP = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); - try { - mLatchAlarms = SP.getBoolean("LatchAlarms",false); - Log.v(TAG,"updatePrefs() - mLatchAlarms = "+mLatchAlarms); - mAudibleFaultWarning = SP.getBoolean("AudibleFaultWarning",true); - Log.v(TAG,"updatePrefs() - mAuidbleFaultWarning = "+mAudibleFaultWarning); - mAudibleAlarm = SP.getBoolean("AudibleAlarm",true); - Log.v(TAG,"updatePrefs() - mAuidbleAlarm = "+mAudibleAlarm); - mAudibleWarning = SP.getBoolean("AudibleWarning",true); - Log.v(TAG,"updatePrefs() - mAuidbleWarning = "+mAudibleWarning); - mSMSAlarm = SP.getBoolean("SMSAlarm",false); - Log.v(TAG,"updatePrefs() - mSMSAlarm = "+mSMSAlarm); - String SMSNumberStr = SP.getString("SMSNumbers",""); - mSMSNumbers = SMSNumberStr.split(","); - mSMSMsgStr = SP.getString("SMSMsg","Seizure Detected!!!"); - Log.v(TAG,"updatePrefs() - SMSNumberStr = "+SMSNumberStr); - Log.v(TAG,"updatePrefs() - mSMSNumbers = "+mSMSNumbers); - mLogAlarms = SP.getBoolean("LogAlarms",true); - Log.v(TAG,"updatePrefs() - mLogAlarms = "+mLogAlarms); - mLogData = SP.getBoolean("LogData",false); - Log.v(TAG,"updatePrefs() - mLogData = "+mLogData); + Log.v(TAG, "updatePrefs()"); + SharedPreferences SP = PreferenceManager + .getDefaultSharedPreferences(getBaseContext()); + try { + mLatchAlarms = SP.getBoolean("LatchAlarms", false); + Log.v(TAG, "updatePrefs() - mLatchAlarms = " + mLatchAlarms); + mAudibleFaultWarning = SP.getBoolean("AudibleFaultWarning", true); + Log.v(TAG, "updatePrefs() - mAuidbleFaultWarning = " + mAudibleFaultWarning); + mAudibleAlarm = SP.getBoolean("AudibleAlarm", true); + Log.v(TAG, "updatePrefs() - mAuidbleAlarm = " + mAudibleAlarm); + mAudibleWarning = SP.getBoolean("AudibleWarning", true); + Log.v(TAG, "updatePrefs() - mAuidbleWarning = " + mAudibleWarning); + mSMSAlarm = SP.getBoolean("SMSAlarm", false); + Log.v(TAG, "updatePrefs() - mSMSAlarm = " + mSMSAlarm); + String SMSNumberStr = SP.getString("SMSNumbers", ""); + mSMSNumbers = SMSNumberStr.split(","); + mSMSMsgStr = SP.getString("SMSMsg", "Seizure Detected!!!"); + Log.v(TAG, "updatePrefs() - SMSNumberStr = " + SMSNumberStr); + Log.v(TAG, "updatePrefs() - mSMSNumbers = " + mSMSNumbers); + mLogAlarms = SP.getBoolean("LogAlarms", true); + Log.v(TAG, "updatePrefs() - mLogAlarms = " + mLogAlarms); + mLogData = SP.getBoolean("LogData", false); + Log.v(TAG, "updatePrefs() - mLogData = " + mLogData); - // Parse the AppRestartTimeout period setting. - try { - String appRestartTimeoutStr = SP.getString("AppRestartTimeout", "10"); - mAppRestartTimeout = Integer.parseInt(appRestartTimeoutStr); - Log.v(TAG, "onStart() - mAppRestartTimeout = " + mAppRestartTimeout); - } catch (Exception ex) { - Log.v(TAG, "onStart() - Problem with AppRestartTimeout preference!"); - Toast toast = Toast.makeText(getApplicationContext(), "Problem Parsing AppRestartTimeout Preference", Toast.LENGTH_SHORT); - toast.show(); - } - - - // Parse the FaultTimer period setting. - try { - String faultTimerPeriodStr = SP.getString("FaultTimerPeriod", "30"); - mFaultTimerPeriod = Integer.parseInt(faultTimerPeriodStr); - Log.v(TAG, "onStart() - mFaultTimerPeriod = " + mFaultTimerPeriod); - } catch (Exception ex) { - Log.v(TAG, "onStart() - Problem with FaultTimerPeriod preference!"); - Toast toast = Toast.makeText(getApplicationContext(), "Problem Parsing FaultTimerPeriod Preference", Toast.LENGTH_SHORT); - toast.show(); - } - - - // Watch Settings - PebbleDictionary setDict = new PebbleDictionary(); - short intVal; - String prefStr; - prefStr = SP.getString("AlarmFreqMin","5"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG, "updatePrefs() AlarmFreqMin = " + intVal); - setDict.addInt16(KEY_ALARM_FREQ_MIN,intVal); - - prefStr = SP.getString("AlarmFreqMax","10"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG, "updatePrefs() AlarmFreqMax = " + intVal); - setDict.addUint16(KEY_ALARM_FREQ_MAX, (short) intVal); - - prefStr = SP.getString("WarnTime","5"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG, "updatePrefs() WarnTime = " + intVal); - setDict.addUint16(KEY_WARN_TIME,(short)intVal); - - prefStr = SP.getString("AlarmTime","10"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG, "updatePrefs() AlarmTime = " + intVal); - setDict.addUint16(KEY_ALARM_TIME,(short)intVal); - - prefStr = SP.getString("AlarmThresh","100"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG, "updatePrefs() AlarmThresh = " + intVal); - setDict.addUint16(KEY_ALARM_THRESH,(short)intVal); - - prefStr = SP.getString("AlarmRatioThresh","30"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG, "updatePrefs() AlarmRatioThresh = " + intVal); - setDict.addUint16(KEY_ALARM_RATIO_THRESH, (short) intVal); - - boolean fallActiveBool = SP.getBoolean("FallActive",false); - Log.v(TAG, "updatePrefs() FallActive = " + fallActiveBool); - if (fallActiveBool) - setDict.addUint16(KEY_FALL_ACTIVE,(short)1); - else - setDict.addUint16(KEY_FALL_ACTIVE,(short)0); - - prefStr = SP.getString("FallThreshMin","200"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG,"updatePrefs() FallThreshMin = "+intVal); - setDict.addUint16(KEY_FALL_THRESH_MIN,(short)intVal); - - prefStr = SP.getString("FallThreshMax","1200"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG,"updatePrefs() FallThreshMax = "+intVal); - setDict.addUint16(KEY_FALL_THRESH_MAX,(short)intVal); - - prefStr = SP.getString("FallWindow","1500"); - intVal = (short)Integer.parseInt(prefStr); - Log.v(TAG,"updatePrefs() FallWindow = "+intVal); - setDict.addUint16(KEY_FALL_WINDOW,(short)intVal); - - - // Send Watch Settings to Pebble - Log.v(TAG,"updatePrefs() - setDict = "+setDict.toJsonString()); - PebbleKit.sendDataToPebble(getApplicationContext(), SD_UUID, setDict); - } catch (Exception ex) { - Log.v(TAG,"updatePrefs() - Problem parsing preferences!"); - Toast toast = Toast.makeText(getApplicationContext(),"Problem Parsing Preferences - Something won't work - Please go back to Settings and correct it!",Toast.LENGTH_SHORT); - toast.show(); - } + } catch (Exception ex) { + Log.v(TAG, "updatePrefs() - Problem parsing preferences!"); + Toast toast = Toast.makeText(getApplicationContext(), "Problem Parsing Preferences - Something won't work - Please go back to Settings and correct it!", Toast.LENGTH_SHORT); + toast.show(); + } } /* Checks if external storage is available for read and write */ public boolean isExternalStorageWritable() { - String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state)) { - return true; - } - return false; + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state)) { + return true; + } + return false; } public File getDataStorageDir() { - // Get the directory for the user's public pictures directory. - File file = - new File(Environment.getExternalStorageDirectory() - ,"OpenSeizureDetector"); - if (!file.mkdirs()) { - Log.e(TAG, "Directory not created"); - } - return file; + // Get the directory for the user's public pictures directory. + File file = + new File(Environment.getExternalStorageDirectory() + , "OpenSeizureDetector"); + if (!file.mkdirs()) { + Log.e(TAG, "Directory not created"); + } + return file; } /** * Write data to SD card alarm log */ public void writeAlarmToSD() { - writeToSD(true); + writeToSD(true); } /** * Write to data log file on SD Card */ public void writeToSD() { - writeToSD(false); + writeToSD(false); } - + /** * Write data to SD card - writes to data log file unless alarm=true, * in which case writes to alarm log file. */ public void writeToSD(boolean alarm) { - Log.v(TAG,"writeToSD("+alarm+")"); - Time tnow = new Time(Time.getCurrentTimezone()); - tnow.setToNow(); - String dateStr = tnow.format("%Y-%m-%d"); + Log.v(TAG, "writeToSD(" + alarm + ")"); + Time tnow = new Time(Time.getCurrentTimezone()); + tnow.setToNow(); + String dateStr = tnow.format("%Y-%m-%d"); - // Select filename depending on 'alarm' parameter. - String fname; - if (alarm) - fname = "AlarmLog"; - else - fname = "DataLog"; + // Select filename depending on 'alarm' parameter. + String fname; + if (alarm) + fname = "AlarmLog"; + else + fname = "DataLog"; - fname = fname+"_"+dateStr+".txt"; - // Open output directory on SD Card. - if (isExternalStorageWritable()) { - try { - FileWriter of = new FileWriter(getDataStorageDir().toString() - + "/" + fname, true); - if (sdData!=null) { - Log.v(TAG,"writing sdData.toString()"); - of.append(sdData.toString()+"\n"); - } - of.close(); - } catch(Exception ex) { - Log.e(TAG,"writeAlarmToSD - error "+ex.toString()); - } - } else { - Log.e(TAG,"ERROR - Can not Write to External Folder"); - } + fname = fname + "_" + dateStr + ".txt"; + // Open output directory on SD Card. + if (isExternalStorageWritable()) { + try { + FileWriter of = new FileWriter(getDataStorageDir().toString() + + "/" + fname, true); + if (mSdData != null) { + Log.v(TAG, "writing mSdData.toString()"); + of.append(mSdData.toString() + "\n"); + } + of.close(); + } catch (Exception ex) { + Log.e(TAG, "writeAlarmToSD - error " + ex.toString()); + } + } else { + Log.e(TAG, "ERROR - Can not Write to External Folder"); + } } /** @@ -912,203 +590,190 @@ public class SdServer extends Service * 8080. */ private class WebServer extends NanoHTTPD { - private String TAG = "WebServer"; - public WebServer() - { - // Set the port to listen on (8080) + private String TAG = "WebServer"; + + public WebServer() { + // Set the port to listen on (8080) super(8080); } @Override - public Response serve(String uri, Method method, + public Response serve(String uri, Method method, Map header, Map parameters, Map files) { - Log.v(TAG,"WebServer.serve() - uri="+uri+" Method="+method.toString()); - String answer = "Error - you should not see this message! - Something wrong in WebServer.serve()"; + Log.v(TAG, "WebServer.serve() - uri=" + uri + " Method=" + method.toString()); + String answer = "Error - you should not see this message! - Something wrong in WebServer.serve()"; - Iterator it = parameters.keySet().iterator(); - while (it.hasNext()) { - Object key = it.next(); - Object value = parameters.get(key); - //Log.v(TAG,"Request parameters - key="+key+" value="+value); - } + Iterator it = parameters.keySet().iterator(); + while (it.hasNext()) { + Object key = it.next(); + Object value = parameters.get(key); + //Log.v(TAG,"Request parameters - key="+key+" value="+value); + } - if (uri.equals("/")) uri = "/index.html"; - switch(uri) { - case "/data": - //Log.v(TAG,"WebServer.serve() - Returning data"); - try { - //JSONObject jsonObj = new JSONObject(); - //jsonObj.put("Time",mPebbleStatusTime.format("%H:%M:%S")); - //jsonObj.put("alarmState",sdData.alarmState); - //jsonObj.put("alarmPhrase",sdData.alarmPhrase); - //jsonObj.put("maxVal",sdData.maxVal); - //jsonObj.put("maxFreq",sdData.maxFreq); - //jsonObj.put("specPower",sdData.specPower); - //jsonObj.put("roiPower",sdData.roiPower); - //jsonObj.put("pebCon",mPebbleConnected); - //jsonObj.put("pebAppRun",mPebbleAppRunning); - answer = sdData.toString(); - } catch (Exception ex) { - Log.v(TAG,"Error Creating Data Object - "+ex.toString()); - answer = "Error Creating Data Object"; - } - break; + if (uri.equals("/")) uri = "/index.html"; + switch (uri) { + case "/data": + //Log.v(TAG,"WebServer.serve() - Returning data"); + try { + answer = mSdData.toString(); + } catch (Exception ex) { + Log.v(TAG, "Error Creating Data Object - " + ex.toString()); + answer = "Error Creating Data Object"; + } + break; - case "/settings": - //Log.v(TAG,"WebServer.serve() - Returning settings"); - try { - JSONObject jsonObj = new JSONObject(); - jsonObj.put("alarmFreqMin",sdData.alarmFreqMin); - jsonObj.put("alarmFreqMax",sdData.alarmFreqMax); - jsonObj.put("nMin",sdData.nMin); - jsonObj.put("nMax",sdData.nMax); - jsonObj.put("warnTime",sdData.warnTime); - jsonObj.put("alarmTime",sdData.alarmTime); - jsonObj.put("alarmThresh",sdData.alarmThresh); - jsonObj.put("alarmRatioThresh",sdData.alarmRatioThresh); - jsonObj.put("batteryPc",sdData.batteryPc); - answer = jsonObj.toString(); - } catch (Exception ex) { - Log.v(TAG,"Error Creating Data Object - "+ex.toString()); - answer = "Error Creating Data Object"; - } - break; + case "/settings": + //Log.v(TAG,"WebServer.serve() - Returning settings"); + try { + JSONObject jsonObj = new JSONObject(); + jsonObj.put("alarmFreqMin", mSdData.alarmFreqMin); + jsonObj.put("alarmFreqMax", mSdData.alarmFreqMax); + jsonObj.put("nMin", mSdData.nMin); + jsonObj.put("nMax", mSdData.nMax); + jsonObj.put("warnTime", mSdData.warnTime); + jsonObj.put("alarmTime", mSdData.alarmTime); + jsonObj.put("alarmThresh", mSdData.alarmThresh); + jsonObj.put("alarmRatioThresh", mSdData.alarmRatioThresh); + jsonObj.put("batteryPc", mSdData.batteryPc); + answer = jsonObj.toString(); + } catch (Exception ex) { + Log.v(TAG, "Error Creating Data Object - " + ex.toString()); + answer = "Error Creating Data Object"; + } + break; - case "/spectrum": - Log.v(TAG,"WebServer.serve() - Returning spectrum - 1"); - try { - JSONObject jsonObj = new JSONObject(); - Log.v(TAG,"WebServer.serve() - Returning spectrum - 2"); - // Initialised it this way because one phone was ok with JSONArray(sdData.simpleSpec), and the other crashed... - JSONArray arr = new JSONArray(); - for (int i=0;i