diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e8cfc4..bfe380e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Improved start-up checks for permissions - Improved system re-start after changing settings (but still not perfect!) - Disabled the CNN algorithm by default as it is causing some false alarms (Issue #170) + - Added watch signal strength history graph and watch battery hisory graph to main activity V4.2.5 - Set BLE device time if the characteristic is available. V4.2.4 - Added checks and a FAULT condition for Bluetooth errors in Bluetooth Data Source V4.2.3 - Uses 3d accelerometer data to calculate magnitude if vector magnitude is not sent from data source. diff --git a/app/release/app-release-4.2.6a.apk b/app/release/app-release-4.2.6.apk similarity index 69% rename from app/release/app-release-4.2.6a.apk rename to app/release/app-release-4.2.6.apk index 27f5ecc..483f38e 100644 Binary files a/app/release/app-release-4.2.6a.apk and b/app/release/app-release-4.2.6.apk differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6305fa1..2370226 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="4.2.6"> diff --git a/app/src/main/java/uk/org/openseizuredetector/FragmentBatt.java b/app/src/main/java/uk/org/openseizuredetector/FragmentBatt.java index 4bb0432..4a40974 100644 --- a/app/src/main/java/uk/org/openseizuredetector/FragmentBatt.java +++ b/app/src/main/java/uk/org/openseizuredetector/FragmentBatt.java @@ -64,7 +64,7 @@ public class FragmentBatt extends FragmentOsdBaseClass { @Override public void onResume() { super.onResume(); - mLineChart = mRootView.findViewById(R.id.lineChart); + mLineChart = mRootView.findViewById(R.id.battLineChart); mLineChart.getLegend().setEnabled(false); XAxis xAxis = mLineChart.getXAxis(); xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); @@ -76,8 +76,8 @@ public class FragmentBatt extends FragmentOsdBaseClass { xAxis.setTextColor(Color.WHITE); YAxis yAxis = mLineChart.getAxisLeft(); - yAxis.setAxisMinValue(40f); - yAxis.setAxisMaxValue(240f); + yAxis.setAxisMinValue(0f); + yAxis.setAxisMaxValue(100f); yAxis.setDrawGridLines(true); yAxis.setDrawLabels(true); yAxis.setTextColor(Color.WHITE); @@ -111,6 +111,7 @@ public class FragmentBatt extends FragmentOsdBaseClass { double watchBattArr[] = mConnection.mSdServer.mSdData.watchBattBuff.getVals(); // This gives us a simple vector of hr values to plot. int nPhoneBattArr = mConnection.mSdServer.mSdData.phoneBattBuff.getNumVals(); double phoneBattArr[] = mConnection.mSdServer.mSdData.phoneBattBuff.getVals(); + Log.i(TAG,"updateUi() - nWatchBattArr="+nWatchBattArr+", nPhoneBattArr="+nPhoneBattArr); if (Objects.nonNull(mConnection.mSdServer.mSdData.watchBattBuff) && nWatchBattArr > 0) { Log.v(TAG, "hrWatchBattBuff.getNumVals=" + nWatchBattArr); lineDataSet.clear(); diff --git a/app/src/main/java/uk/org/openseizuredetector/FragmentCommon.java b/app/src/main/java/uk/org/openseizuredetector/FragmentCommon.java index 179f4e4..6c4226b 100644 --- a/app/src/main/java/uk/org/openseizuredetector/FragmentCommon.java +++ b/app/src/main/java/uk/org/openseizuredetector/FragmentCommon.java @@ -193,10 +193,11 @@ public class FragmentCommon extends FragmentOsdBaseClass { tv.setText(getString(R.string.DataSource) + " = " + "Phone (Demo Mode)"); tv.setBackgroundColor(warnColour); tv.setTextColor(warnTextColour); - } else if (mConnection.mSdServer.mSdDataSourceName.equals("BLE")) { + } else if (mConnection.mSdServer.mSdDataSourceName.equals("BLE") + || mConnection.mSdServer.mSdDataSourceName.equals("BLE2")) { tv.setText(getString(R.string.DataSource) + " = " + mConnection.mSdServer.mSdDataSourceName + " ("+ mConnection.mSdServer.mSdData.watchSdName + ", " - + mConnection.mSdServer.mSdData.watchPartNo+")"); + + mConnection.mSdServer.mSdData.watchSerNo+")"); } else { tv.setText(getString(R.string.DataSource) + " = " + mConnection.mSdServer.mSdDataSourceName); } diff --git a/app/src/main/java/uk/org/openseizuredetector/FragmentSystem.java b/app/src/main/java/uk/org/openseizuredetector/FragmentSystem.java index ef6d18f..95917ab 100644 --- a/app/src/main/java/uk/org/openseizuredetector/FragmentSystem.java +++ b/app/src/main/java/uk/org/openseizuredetector/FragmentSystem.java @@ -174,6 +174,20 @@ public class FragmentSystem extends FragmentOsdBaseClass { tv.setBackgroundColor(okColour); tv.setTextColor(okTextColour); } + tv = (TextView) mRootView.findViewById(R.id.watch_manuf_tv); + tv.setText(mConnection.mSdServer.mSdData.watchManuf); + tv = (TextView) mRootView.findViewById(R.id.watch_partno_tv); + tv.setText(mConnection.mSdServer.mSdData.watchPartNo); + tv = (TextView) mRootView.findViewById(R.id.watch_fwver_tv); + tv.setText(mConnection.mSdServer.mSdData.watchFwVersion); + tv = (TextView) mRootView.findViewById(R.id.watch_sdname_tv); + tv.setText(mConnection.mSdServer.mSdData.watchSdName); + tv = (TextView) mRootView.findViewById(R.id.watch_sdver_tv); + tv.setText(mConnection.mSdServer.mSdData.watchSdVersion); + tv = (TextView) mRootView.findViewById(R.id.watch_batt_tv); + tv.setText(mConnection.mSdServer.mSdData.batteryPc+" %"); + tv = (TextView) mRootView.findViewById(R.id.watch_signal_tv); + tv.setText(String.format("%.0f dB", mConnection.mSdServer.mSdData.watchSignalStrength)); } } catch (Exception e) { Log.e(TAG, "UpdateUi: Exception - "); diff --git a/app/src/main/java/uk/org/openseizuredetector/FragmentWatchSig.java b/app/src/main/java/uk/org/openseizuredetector/FragmentWatchSig.java new file mode 100644 index 0000000..dfc82ab --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/FragmentWatchSig.java @@ -0,0 +1,148 @@ +package uk.org.openseizuredetector; + +import android.graphics.Color; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.appcompat.widget.SwitchCompat; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +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.utils.ValueFormatter; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + + +public class FragmentWatchSig extends FragmentOsdBaseClass { + String TAG = "FragmentWatchSig"; + + LineChart mLineChart; + LineData lineData; + LineDataSet lineDataSet; + List sigHistory = new ArrayList<>(); + List hrHistoryStrings = new ArrayList<>(); + + private TextView tvCurrSigStren; + + public FragmentWatchSig() { + // Required empty public constructor + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + lineDataSet = new LineDataSet(new ArrayList(), "Watch Signal Strength history"); + //lineDataSet.setColors(ColorTemplate.JOYFUL_COLORS); + lineDataSet.setValueTextColor(Color.BLACK); + lineDataSet.setValueTextSize(18f); + lineDataSet.setDrawValues(false); + lineDataSet.setCircleSize(0f); + lineDataSet.setLineWidth(3f); + //lineDataSetAverage = new LineDataSet(new ArrayList(),"Heart rate history" ); + //lineDataSetAverage.setColors(ColorTemplate.JOYFUL_COLORS); + //lineDataSetAverage.setValueTextColor(Color.BLACK); + //lineDataSetAverage.setValueTextSize(18f); + + } + + @Override + public void onResume() { + super.onResume(); + mLineChart = mRootView.findViewById(R.id.sigStrengthLineChart); + mLineChart.getLegend().setEnabled(false); + XAxis xAxis = mLineChart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + xAxis.setTextSize(10f); + xAxis.setDrawAxisLine(true); + xAxis.setDrawLabels(true); + // Note: the default text colour is BLACK, so does not show up on black background!!! + // This took a lot of finding.... + xAxis.setTextColor(Color.WHITE); + + YAxis yAxis = mLineChart.getAxisLeft(); + //yAxis.setAxisMinValue(40f); + //yAxis.setAxisMaxValue(240f); + yAxis.setDrawGridLines(true); + yAxis.setDrawLabels(true); + yAxis.setTextColor(Color.WHITE); + // Inhibit the decimal part of the y axis labels. + yAxis.setValueFormatter(new ValueFormatter() { + @Override + public String getFormattedValue(float v) { + DecimalFormat format = new DecimalFormat("###"); + return format.format(v); + } + }); + + YAxis yAxis2 = mLineChart.getAxisRight(); + yAxis2.setDrawGridLines(false); + yAxis2.setEnabled(false); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_watch_sig, container, false); + } + + @Override + protected void updateUi() { + Log.d(TAG, "updateUi()"); + tvCurrSigStren = (TextView) mRootView.findViewById(R.id.current_sig_strength_tv); + if (mConnection.mBound) { + if (Objects.nonNull(tvCurrSigStren)) + tvCurrSigStren.setText(String.valueOf((int) mConnection.mSdServer.mSdData.watchSignalStrength)); + double histArr[] = mConnection.mSdServer.mSdData.watchSignalStrengthBuff.getVals(); + int nHist = histArr.length; + if (Objects.nonNull(histArr) && nHist > 0) { + Log.v(TAG, "nHist=" + nHist); + lineDataSet.clear(); + String xVals[] = new String[nHist]; + for (int i = 0; i < nHist; i++) { + //Log.d(TAG,"i="+i+", HR="+hrHistArr[i]); + xVals[i] = String.valueOf(i); + lineDataSet.addEntry(new Entry((float) histArr[i], i)); + } + Log.d(TAG, "xVals=" + Arrays.toString(xVals) + ", lneDataSet=" + lineDataSet.toSimpleString()); + lineDataSet.setColors(new int[]{0xffff0000}); + LineData histLineData = new LineData(xVals, lineDataSet); + + + mLineChart.setData(histLineData); + mLineChart.getData().notifyDataChanged(); + mLineChart.notifyDataSetChanged(); + mLineChart.refreshDrawableState(); + float xSpan = (nHist * 5.0f) / 60.0f; // time in minutes assuming one point every 5 seconds. + mLineChart.setDescription("Signal Strength History " + + String.format("%.1f", xSpan) + + " " + getString(R.string.minutes)); + mLineChart.setDescriptionTextSize(12f); + mLineChart.invalidate(); + //if (mConnection.mBound){ + // lineChart.postInvalidate(); + //} + } + + } else { + Log.w(TAG,"not Bound to Server"); + return; + } + + + } + +} diff --git a/app/src/main/java/uk/org/openseizuredetector/MainActivity2.java b/app/src/main/java/uk/org/openseizuredetector/MainActivity2.java index bdc4576..a423152 100644 --- a/app/src/main/java/uk/org/openseizuredetector/MainActivity2.java +++ b/app/src/main/java/uk/org/openseizuredetector/MainActivity2.java @@ -318,8 +318,10 @@ public class MainActivity2 extends AppCompatActivity { return new FragmentHrAlg(); case 2: return new FragmentSystem(); - //case 3: - // return new FragmentBatt(); + case 3: + return new FragmentWatchSig(); + case 4: + return new FragmentBatt(); //case 4: // return new FragmentDataSharing(); @@ -331,7 +333,7 @@ public class MainActivity2 extends AppCompatActivity { @Override public int getItemCount() { - return 3; + return 5; } } diff --git a/app/src/main/java/uk/org/openseizuredetector/SdData.java b/app/src/main/java/uk/org/openseizuredetector/SdData.java index 03f68a3..79f04a0 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdData.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdData.java @@ -69,6 +69,7 @@ public class SdData implements Parcelable { public CircBuf watchBattBuff = new CircBuf(24*3600/5, -1); // 24 hour buffer public CircBuf phoneBattBuff = new CircBuf(24*3600/5, -1); // 24 hour buffer + public CircBuf watchSignalStrengthBuff = new CircBuf(4*3600/5, -1); // 4 hour buffer /* Heart Rate Alarm Settings */ public boolean mHRAlarmActive = false; @@ -136,12 +137,14 @@ public class SdData implements Parcelable { public double mO2Sat = 0; public double mPseizure = 0.; + public float watchSignalStrength; public SdData() { simpleSpec = new int[10]; rawData = new double[N_RAW_DATA]; rawData3D = new double[N_RAW_DATA * 3]; dataTime = new Time(Time.getCurrentTimezone()); + dataTime.setToNow(); timeDiff = 0f; } @@ -162,8 +165,12 @@ public class SdData implements Parcelable { // FIXME - this doesn't work!!! Time tnow = new Time(); tnow.setToNow(); - timeDiff = (tnow.toMillis(false) - - dataTime.toMillis(false))/1000f; + if (dataTime != null) { + timeDiff = (tnow.toMillis(false) + - dataTime.toMillis(false)) / 1000f; + } else { + timeDiff = 0f; + } dataTime.setToNow(); Log.v(TAG, "fromJSON(): dataTime = " + dataTime.toString()); maxVal = jo.optInt("maxVal"); @@ -322,6 +329,7 @@ public class SdData implements Parcelable { jsonObj.put("watchSdName", watchSdName); jsonObj.put("watchFwVersion", watchFwVersion); jsonObj.put("watchSdVersion", watchSdVersion); + jsonObj.put("watchSignalStrength", watchSignalStrength); retval = jsonObj.toString(); } catch (Exception ex) { diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java index 4e48e75..91c91a9 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSource.java @@ -390,7 +390,6 @@ public abstract class SdDataSource { mSamplePeriod = (short) dataObject.getInt("analysisPeriod"); mSampleFreq = (short) dataObject.getInt("sampleFreq"); mSdData.batteryPc = (short) dataObject.getInt("battery"); - mSdData.watchBattBuff.add(mSdData.batteryPc); Log.v(TAG, "updateFromJSON - mSamplePeriod=" + mSamplePeriod + " mSampleFreq=" + mSampleFreq); mUtil.writeToSysLogFile("SDDataSource.updateFromJSON - Settings Received"); @@ -475,6 +474,7 @@ public abstract class SdDataSource { // Update phone battery level - it is done here so it is called for all data sources. mSdData.phoneBatteryPc = getPhoneBatteryLevel(); mSdData.phoneBattBuff.add(mSdData.phoneBatteryPc); + mSdData.watchBattBuff.add(mSdData.batteryPc); try { // FIXME - Use specified sampleFreq, not this hard coded one mSampleFreq = 25; @@ -538,8 +538,12 @@ public abstract class SdDataSource { mSdData.roiPower = (long) roiPower / ACCEL_SCALE_FACTOR; Time tnow = new Time(); tnow.setToNow(); - mSdData.timeDiff = (tnow.toMillis(false) - - mSdData.dataTime.toMillis(false))/1000f; + if (mSdData.dataTime != null) { + mSdData.timeDiff = (tnow.toMillis(false) + - mSdData.dataTime.toMillis(false)) / 1000f; + } else { + mSdData.timeDiff = 0f; + } mSdData.dataTime.setToNow(); mSdData.dataTime.setToNow(); diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java index 519e8a9..addabaa 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceBLE2.java @@ -153,8 +153,8 @@ public class SdDataSourceBLE2 extends SdDataSource { // FIXME: Read the shared preferences in this class so SdDataSource does not need to know // FIXME: about BLE details. Log.i(TAG, "mBLEDevice is " + mBleDeviceName + ", Addr=" + mBleDeviceAddr); - mSdData.watchSdName = mBleDeviceName; - mSdData.watchPartNo = mBleDeviceAddr; + //mSdData.watchSdName = mBleDeviceName; + mSdData.watchSerNo = mBleDeviceAddr; boolean success = CurrentTimeService.startServer(mContext); @@ -231,6 +231,8 @@ public class SdDataSourceBLE2 extends SdDataSource { peripheral.setPreferredPhy(PhyType.LE_CODED, PhyType.LE_CODED, PhyOptions.S8); peripheral.readPhy(); + peripheral.readRemoteRssi(); + boolean foundOsdService = false; for (BluetoothGattService service : peripheral.getServices()) { String servUuidStr = service.getUuid().toString(); @@ -420,6 +422,7 @@ public class SdDataSourceBLE2 extends SdDataSource { mDataStatusTime = new Time(Time.getCurrentTimezone()); // Process the data to do seizure detection doAnalysis(); + mBlePeripheral.readRemoteRssi(); // Update RSSI // Re-start collecting raw data. nRawData = 0; // Notify the device of the resulting alarm state @@ -461,7 +464,8 @@ public class SdDataSourceBLE2 extends SdDataSource { byte[] rawDataBytes = characteristic.getValue(); String watchSerNo = new String(rawDataBytes, StandardCharsets.UTF_8); Log.i(TAG, "Received Watch Serial No.: " + watchSerNo); - mSdData.watchSerNo = watchSerNo; + //mSdData.watchSerNo = watchSerNo; + // We do not use this serial number because it is zero for PineTime - we set the MAC address at start-up instead. } else if (charUuidStr.equals(CHAR_DEV_HW_VER)) { byte[] rawDataBytes = characteristic.getValue(); String watchHwVer = new String(rawDataBytes, StandardCharsets.UTF_8); @@ -490,6 +494,12 @@ public class SdDataSourceBLE2 extends SdDataSource { Log.i(TAG, String.format("new MTU set: %d", mtu)); } + @Override + public void onReadRemoteRssi(@NotNull BluetoothPeripheral peripheral, int rssi, @NotNull GattStatus status) { + Log.d(TAG, String.format("Rssi = %d", rssi)); + mSdData.watchSignalStrength = rssi; + mSdData.watchSignalStrengthBuff.add(rssi); + } }; diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index 9bb6f19..cba11af 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -821,15 +821,19 @@ public class SdServer extends Service implements SdDataReceiver { // flag. if (mFaultTimerCompleted) { faultWarningBeep(); - // Re-start the data source to see if that fixes it - Log.w(TAG,"FAULT - stopping data source"); - mSdDataSource.stop(); - mHandler.postDelayed(new Runnable() { - public void run() { - Log.w(TAG,"FAULT - restarting data source"); - mSdDataSource.start(); - } - }, 10000); + // Disable the data-source re-start for now because it was messing up BLE2 data source by ending up with multiple + // notifications for the same data when it reconnects. + if (false) { + // Re-start the data source to see if that fixes it + Log.w(TAG, "FAULT - stopping data source"); + mSdDataSource.stop(); + mHandler.postDelayed(new Runnable() { + public void run() { + Log.w(TAG, "FAULT - restarting data source"); + mSdDataSource.start(); + } + }, 10000); + } } else { startFaultTimer(); Log.v(TAG, "onSdDataFault() - starting Fault Timer"); diff --git a/app/src/main/res/layout/fragment_batt.xml b/app/src/main/res/layout/fragment_batt.xml index 4a6e11e..92afc40 100644 --- a/app/src/main/res/layout/fragment_batt.xml +++ b/app/src/main/res/layout/fragment_batt.xml @@ -19,7 +19,7 @@ diff --git a/app/src/main/res/layout/fragment_system.xml b/app/src/main/res/layout/fragment_system.xml index 235d8b6..dbdc25c 100644 --- a/app/src/main/res/layout/fragment_system.xml +++ b/app/src/main/res/layout/fragment_system.xml @@ -64,11 +64,6 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_watch_sig.xml b/app/src/main/res/layout/fragment_watch_sig.xml new file mode 100644 index 0000000..640d314 --- /dev/null +++ b/app/src/main/res/layout/fragment_watch_sig.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6088ff6..5bb015e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,7 +2,8 @@ OpenSeizureDetector - "\nV4.2.4 - Fault alarm rather than crash if bluetooth system crashes. + "\nV4.2.6 - Fixed issues with Android 13 notifications and BLE data source shutdown. Added support for V2.x of Garmin watch app, Added new BLE2 data source, signal strength and battery level graphs. + \nV4.2.4 - Fault alarm rather than crash if bluetooth system crashes. \nV4.2.3 - Bug Fixes (heart rate alarm and latched alarm issues) \nV4.2 - Added support for PineTime Watches using Bluetooth data source. \n - Added new, swipeable user interface to simplify the main screen..