diff --git a/app/build.gradle b/app/build.gradle index cfea828..1bf48c9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,7 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + buildToolsVersion '29.0.2' } dependencies { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 069609f..66c7269 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -40,7 +40,7 @@ - + diff --git a/app/src/main/java/uk/org/openseizuredetector/AccelData.java b/app/src/main/java/uk/org/openseizuredetector/AccelData.java deleted file mode 100644 index 2eabed3..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/AccelData.java +++ /dev/null @@ -1,85 +0,0 @@ -package uk.org.openseizuredetector; - -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.TimeZone; - -/** - * Created by graham on 27/06/16. - */ -/* From https://github.com/kramimus/pebble-accel-analyzer */ -public class AccelData { - private final String TAG = AccelData.class.getSimpleName(); - - final private int x; - final private int y; - final private int z; - - private long timestamp = 0; - final private boolean didVibrate; - - public AccelData(byte[] data) { - x = (data[0] & 0xff) | (data[1] << 8); - y = (data[2] & 0xff) | (data[3] << 8); - z = (data[4] & 0xff) | (data[5] << 8); - didVibrate = data[6] != 0; - - for (int i = 0; i < 8; i++) { - timestamp |= ((long)(data[i+7] & 0xff)) << (i * 8); - } - } - - public JSONObject toJson() { - JSONObject json = new JSONObject(); - try { - json.put("x", x); - json.put("y", y); - json.put("z", z); - json.put("ts", timestamp); - json.put("v", didVibrate); - return json; - } catch (JSONException e) { - Log.w(TAG, "problem constructing accel data, skipping " + e); - } - return null; - } - - public static List fromDataArray(byte[] data) { - List accels = new ArrayList(); - for (int i = 0; i < data.length; i += 15) { - accels.add(new AccelData(Arrays.copyOfRange(data, i, i + 15))); - } - return accels; - } - - public long getTimestamp() { - return timestamp; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public int getZ() { - return z; - } - - public int getMagnitude() { - return (int)Math.sqrt(x*x + y*y + z*z); - } - - public void applyTimezone(TimeZone tz) { - timestamp -= tz.getOffset(timestamp); - } -} - diff --git a/app/src/main/java/uk/org/openseizuredetector/AuthCallbackInterface.java b/app/src/main/java/uk/org/openseizuredetector/AuthCallbackInterface.java deleted file mode 100644 index 8422ffe..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/AuthCallbackInterface.java +++ /dev/null @@ -1,6 +0,0 @@ -package uk.org.openseizuredetector; - -// Interface used by the authentication part of WebApi to send back the authentication token -public interface AuthCallbackInterface { - void authCallback(boolean success, String tokenStr); -} diff --git a/app/src/main/java/uk/org/openseizuredetector/AuthDialogInterface.java b/app/src/main/java/uk/org/openseizuredetector/AuthDialogInterface.java deleted file mode 100644 index f59b062..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/AuthDialogInterface.java +++ /dev/null @@ -1,5 +0,0 @@ -package uk.org.openseizuredetector; - -public interface AuthDialogInterface { - void onDialogDone(boolean state); -} \ No newline at end of file diff --git a/app/src/main/java/uk/org/openseizuredetector/AuthenticateActivity.java b/app/src/main/java/uk/org/openseizuredetector/AuthenticateActivity.java index f103e6b..8b27303 100644 --- a/app/src/main/java/uk/org/openseizuredetector/AuthenticateActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/AuthenticateActivity.java @@ -2,6 +2,8 @@ package uk.org.openseizuredetector; import android.content.Context; import android.content.SharedPreferences; +import android.os.Handler; +import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; @@ -12,20 +14,28 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; -public class AuthenticateActivity extends AppCompatActivity - implements AuthCallbackInterface, EventCallbackInterface, DatapointCallbackInterface { +import org.json.JSONObject; + +public class AuthenticateActivity extends AppCompatActivity { private String TAG = "AuthenticateActivity"; - private Context mContext; private EditText mUnameEt; private EditText mPasswdEt; private WebApiConnection mWac; private LogManager mLm; + private SdServiceConnection mConnection; + private OsdUtil mUtil; + final Handler serverStatusHandler = new Handler(); + private String TOKEN_ID = "webApiAuthToken"; @Override protected void onCreate(Bundle savedInstanceState) { Log.v(TAG, "onCreate()"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_authenticate); + + mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler); + mConnection = new SdServiceConnection(getApplicationContext()); + Button cancelBtn = (Button) findViewById(R.id.cancelBtn); cancelBtn.setOnClickListener(onCancel); @@ -39,21 +49,47 @@ public class AuthenticateActivity extends AppCompatActivity mUnameEt = (EditText) findViewById(R.id.username); mPasswdEt = (EditText) findViewById(R.id.password); - mWac = new WebApiConnection(this, this, this, this); - mLm = new LogManager(this); + //mWac = new WebApiConnection(this, String tokenStr); + //mLm = new LogManager(this); } @Override protected void onStart() { super.onStart(); + mUtil.bindToServer(getApplicationContext(), mConnection); + waitForConnection(); + updateUi(); } - public void authCallback(boolean authSuccess, String tokenStr) { - Log.v(TAG,"authCallback"); - updateUi(); + private void waitForConnection() { + // We want the UI to update as soon as it is displayed, but it takes a finite time for + // the mConnection to bind to the service, so we delay half a second to give it chance + // to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer) + if (mConnection.mBound) { + Log.v(TAG, "waitForConnection - Bound!"); + initialiseServiceConnection(); + } else { + Log.v(TAG, "waitForConnection - waiting..."); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + waitForConnection(); + } + }, 100); + } } + private void initialiseServiceConnection() { + mLm = mConnection.mSdServer.mLm; + mWac = mConnection.mSdServer.mLm.mWac; + } + + //public void authCallback(boolean authSuccess, String tokenStr) { + //Log.v(TAG,"authCallback"); + // updateUi(); + //} + public void eventCallback(boolean success, String eventStr) { Log.v(TAG,"eventCallback"); } @@ -68,7 +104,7 @@ public class AuthenticateActivity extends AppCompatActivity LinearLayout loginLl = (LinearLayout)findViewById(R.id.login_ui); LinearLayout logoutLl = (LinearLayout)findViewById(R.id.logout_ui); Log.i(TAG, "switchUi()"); - storedAuthToken = mWac.getStoredToken(); + storedAuthToken = getAuthToken(); //mWac.getStoredToken(); //prefs = PreferenceManager.getDefaultSharedPreferences(this); //storedAuthToken = (prefs.getString("webApiAuthToken", null)); Log.v(TAG, "storedAuthToken=" + storedAuthToken); @@ -107,7 +143,13 @@ public class AuthenticateActivity extends AppCompatActivity String uname = mUnameEt.getText().toString(); String passwd = mPasswdEt.getText().toString(); Log.v(TAG,"onOK() - uname="+uname+", passwd="+passwd); - mWac.authenticate(uname,passwd); + mWac.authenticate(uname,passwd, (String retVal) -> { + if (retVal != null) { + Log.d(TAG,"Authentication Success - token is "+retVal); + saveAuthToken(retVal); + updateUi(); + } + }); //finish(); } }; @@ -119,7 +161,23 @@ public class AuthenticateActivity extends AppCompatActivity Log.v(TAG, "onLogout"); //m_status=false; mWac.logout(); + saveAuthToken(null); updateUi(); } }; + + private void saveAuthToken(String tokenStr) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + prefs.edit().putString(TOKEN_ID, tokenStr).commit(); + mWac.setStoredToken(tokenStr); + } + + public String getAuthToken() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + String authToken = prefs.getString(TOKEN_ID, null); + return authToken; + } + + + } \ No newline at end of file diff --git a/app/src/main/java/uk/org/openseizuredetector/CircularArrayList.java b/app/src/main/java/uk/org/openseizuredetector/CircularArrayList.java deleted file mode 100644 index 08d7d10..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/CircularArrayList.java +++ /dev/null @@ -1,107 +0,0 @@ -package uk.org.openseizuredetector; - -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.RandomAccess; - -/** - * Created by graham on 28/06/16. - */ -public class CircularArrayList - extends AbstractList implements RandomAccess { - /** - * If you use this code, please consider notifying isak at du-preez dot com - * with a brief description of your application. - * - * This is free and unencumbered software released into the public domain. - * Anyone is free to copy, modify, publish, use, compile, sell, or - * distribute this software, either in source code form or as a compiled - * binary, for any purpose, commercial or non-commercial, and by any - * means. - */ - - private final int n; // buffer length - private final List buf; // a List implementing RandomAccess - private int head = 0; - private int tail = 0; - - public CircularArrayList(int capacity) { - n = capacity + 1; - buf = new ArrayList(Collections.nCopies(n, (E) null)); - } - - public int capacity() { - return n - 1; - } - - private int wrapIndex(int i) { - int m = i % n; - if (m < 0) { // java modulus can be negative - m += n; - } - return m; - } - - // This method is O(n) but will never be called if the - // CircularArrayList is used in its typical/intended role. - private void shiftBlock(int startIndex, int endIndex) { - assert (endIndex > startIndex); - for (int i = endIndex - 1; i >= startIndex; i--) { - set(i + 1, get(i)); - } - } - - @Override - public int size() { - return tail - head + (tail < head ? n : 0); - } - - @Override - public E get(int i) { - if (i < 0 || i >= size()) { - throw new IndexOutOfBoundsException(); - } - return buf.get(wrapIndex(head + i)); - } - - @Override - public E set(int i, E e) { - if (i < 0 || i >= size()) { - throw new IndexOutOfBoundsException(); - } - return buf.set(wrapIndex(head + i), e); - } - - @Override - public void add(int i, E e) { - int s = size(); - if (s == n - 1) { - throw new IllegalStateException("Cannot add element." - + " CircularArrayList is filled to capacity."); - } - if (i < 0 || i > s) { - throw new IndexOutOfBoundsException(); - } - tail = wrapIndex(tail + 1); - if (i < s) { - shiftBlock(i, s); - } - set(i, e); - } - - @Override - public E remove(int i) { - int s = size(); - if (i < 0 || i >= s) { - throw new IndexOutOfBoundsException(); - } - E e = get(i); - if (i > 0) { - shiftBlock(0, i); - } - head = wrapIndex(head + 1); - return e; - } -} diff --git a/app/src/main/java/uk/org/openseizuredetector/DatapointCallbackInterface.java b/app/src/main/java/uk/org/openseizuredetector/DatapointCallbackInterface.java deleted file mode 100644 index de4fddc..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/DatapointCallbackInterface.java +++ /dev/null @@ -1,7 +0,0 @@ -package uk.org.openseizuredetector; - -public interface DatapointCallbackInterface { - // Interface is called when a new datapoint is created in the database. - void datapointCallback(boolean success, String eventStr); - } - diff --git a/app/src/main/java/uk/org/openseizuredetector/EditEventActivity.java b/app/src/main/java/uk/org/openseizuredetector/EditEventActivity.java index 08e4c0c..3c4282b 100644 --- a/app/src/main/java/uk/org/openseizuredetector/EditEventActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/EditEventActivity.java @@ -27,12 +27,12 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; -public class EditEventActivity extends AppCompatActivity - implements AuthCallbackInterface, EventCallbackInterface, DatapointCallbackInterface { +public class EditEventActivity extends AppCompatActivity { private String TAG = "EditEventActivity"; private Context mContext; private WebApiConnection mWac; private LogManager mLm; + private SdServiceConnection mConnection; final Handler serverStatusHandler = new Handler(); private OsdUtil mUtil; private List mEventTypesList = null; @@ -54,9 +54,11 @@ public class EditEventActivity extends AppCompatActivity Log.v(TAG, "onCreate()"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_edit_event); + mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler); + mConnection = new SdServiceConnection(getApplicationContext()); - mWac = new WebApiConnection(this, this, this, this); - mLm = new LogManager(this); + //mWac = new WebApiConnection(this, this, this, this); + //mLm = new LogManager(this); Bundle extras = getIntent().getExtras(); @@ -64,25 +66,8 @@ public class EditEventActivity extends AppCompatActivity Long eventId = extras.getLong("eventId"); mEventId = eventId; Log.v(TAG, "onCreate - mEventId=" + mEventId); - try { - mWac.getEvent(mEventId, (JSONObject eventObj) -> { - Log.v(TAG,"onCreate.getEvent"); - if (eventObj != null) { - mEventObj = eventObj; - Log.v(TAG, "onCreate.getEvent: eventObj=" + eventObj.toString()); - updateUi(); - // FIXME: modify updateUi to use mEventObj - } else { - mUtil.showToast("Failed to Retrieve Event from Remote Database"); - finish(); - } - }); - } catch (Exception e) { - Log.e(TAG,"ERROR:"+e.getMessage()); - e.printStackTrace(); - } } - mUtil = new OsdUtil(this, serverStatusHandler); + Button cancelBtn = (Button) findViewById(R.id.cancelBtn); @@ -96,13 +81,47 @@ public class EditEventActivity extends AppCompatActivity mEventSubTypeRg.setOnCheckedChangeListener(onEventSubTypeChange); + } + + @Override + protected void onStart() { + super.onStart(); + Log.v(TAG, "onStart()"); + mUtil.bindToServer(getApplicationContext(), mConnection); + waitForConnection(); + + updateUi(); + } + + private void waitForConnection() { + // We want the UI to update as soon as it is displayed, but it takes a finite time for + // the mConnection to bind to the service, so we delay half a second to give it chance + // to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer) + if (mConnection.mBound) { + Log.v(TAG, "waitForConnection - Bound!"); + initialiseServiceConnection(); + } else { + Log.v(TAG, "waitForConnection - waiting..."); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + waitForConnection(); + } + }, 100); + } + } + + private void initialiseServiceConnection() { + mLm = mConnection.mSdServer.mLm; + mWac = mConnection.mSdServer.mLm.mWac; + // Retrieve the JSONObject containing the standard event types. // Note this obscure syntax is to avoid having to create another interface, so it is worth it :) // See https://medium.com/@pra4mesh/callback-function-in-java-20fa48b27797 mWac.getEventTypes((JSONObject eventTypesObj) -> { - Log.v(TAG, "onCreate.onEventTypesReceived"); + Log.v(TAG, "initialiseServiceConnection().onEventTypesReceived"); if (eventTypesObj == null) { - Log.e(TAG, "onCreate.getEventTypes Callback: Error Retrieving event types"); + Log.e(TAG, "initialiseServiceConnection().getEventTypes Callback: Error Retrieving event types"); mUtil.showToast("Error Retrieving Event Types from Server - Please Try Again Later!"); } else { Iterator keys = eventTypesObj.keys(); @@ -110,7 +129,7 @@ public class EditEventActivity extends AppCompatActivity mEventSubTypesHashMap = new HashMap>(); while (keys.hasNext()) { String key = keys.next(); - Log.v(TAG, "onCreate.getEventTypes Callback: key=" + key); + Log.v(TAG, "initialiseServiceConnection().getEventTypes Callback: key=" + key); mEventTypesList.add(key); try { JSONArray eventSubTypes = eventTypesObj.getJSONArray(key); @@ -121,33 +140,37 @@ public class EditEventActivity extends AppCompatActivity mEventSubTypesHashMap.put(key, eventSubtypesList); mEventTypesListChanged = true; } catch (JSONException e) { - Log.e(TAG, "onCreate(getEventTypes Callback: Error parsing JSONObject" + e.getMessage() + e.toString()); + Log.e(TAG, "initialiseServiceConnection().getEventTypes Callback: Error parsing JSONObject" + e.getMessage() + e.toString()); } } updateUi(); } }); + + // Retrieve the event data to edit + try { + mWac.getEvent(mEventId, (JSONObject eventObj) -> { + Log.v(TAG,"onCreate.getEvent"); + if (eventObj != null) { + mEventObj = eventObj; + Log.v(TAG, "onCreate.getEvent: eventObj=" + eventObj.toString()); + updateUi(); + // FIXME: modify updateUi to use mEventObj + } else { + mUtil.showToast("Failed to Retrieve Event from Remote Database"); + finish(); + } + }); + } catch (Exception e) { + Log.e(TAG,"ERROR:"+e.getMessage()); + e.printStackTrace(); + } + + } - @Override - protected void onStart() { - super.onStart(); - Log.v(TAG, "onStart()"); - updateUi(); - } - public void authCallback(boolean authSuccess, String tokenStr) { - Log.v(TAG, "authCallback"); - updateUi(); - } - public void eventCallback(boolean success, String eventStr) { - Log.v(TAG, "eventCallback"); - } - - public void datapointCallback(boolean success, String datapointStr) { - Log.v(TAG, "datapointCallback"); - } private void updateUi() { Log.v(TAG, "updateUI"); diff --git a/app/src/main/java/uk/org/openseizuredetector/EventCallbackInterface.java b/app/src/main/java/uk/org/openseizuredetector/EventCallbackInterface.java deleted file mode 100644 index 01735a9..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/EventCallbackInterface.java +++ /dev/null @@ -1,6 +0,0 @@ -package uk.org.openseizuredetector; - -public interface EventCallbackInterface { - // Interface is called when a new event is created in the database. - void eventCallback(boolean success, String eventStr); - } diff --git a/app/src/main/java/uk/org/openseizuredetector/DBQueryActivity.java b/app/src/main/java/uk/org/openseizuredetector/ExportDataActivity.java similarity index 97% rename from app/src/main/java/uk/org/openseizuredetector/DBQueryActivity.java rename to app/src/main/java/uk/org/openseizuredetector/ExportDataActivity.java index d653e6b..c64e459 100644 --- a/app/src/main/java/uk/org/openseizuredetector/DBQueryActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/ExportDataActivity.java @@ -15,9 +15,9 @@ import android.widget.TimePicker; import java.util.Calendar; -public class DBQueryActivity extends AppCompatActivity +public class ExportDataActivity extends AppCompatActivity implements View.OnClickListener { - String TAG = "DBQueryActivity"; + String TAG = "ExportDataActivity"; Button mDateBtn; Button mTimeBtn; Button mExportBtn; diff --git a/app/src/main/java/uk/org/openseizuredetector/LogManager.java b/app/src/main/java/uk/org/openseizuredetector/LogManager.java index ce5b309..d0d6e73 100644 --- a/app/src/main/java/uk/org/openseizuredetector/LogManager.java +++ b/app/src/main/java/uk/org/openseizuredetector/LogManager.java @@ -70,12 +70,13 @@ import static android.database.sqlite.SQLiteDatabase.openOrCreateDatabase; * - Upload the datapoints, linking them to the new eventID. * - Mark all the uploaded datapoints as uploaded. */ -public class LogManager implements AuthCallbackInterface, EventCallbackInterface, DatapointCallbackInterface { +public class LogManager { private String TAG = "LogManager"; private String mDbName = "osdData"; private String mDbTableName = "datapoints"; private boolean mLogRemote; private boolean mLogRemoteMobile; + private String mAuthToken; private OsdDbHelper mOSDDb; private RemoteLogTimer mRemoteLogTimer; private Context mContext; @@ -94,38 +95,33 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface private AutoPruneTimer mAutoPruneTimer; - public LogManager(Context context) { + public LogManager(Context context, + boolean logRemote, boolean logRemoteMobile, String authToken, + long eventDuration, long remoteLogPeriod, + boolean autoPruneDb, long dataRetentionPeriod) { String prefVal; Log.d(TAG, "LogManger Constructor"); mContext = context; Handler handler = new Handler(); - SharedPreferences prefs; - prefs = PreferenceManager.getDefaultSharedPreferences(mContext); - mLogRemote = (prefs.getBoolean("LogDataRemote", false)); + mLogRemote = logRemote; + mLogRemoteMobile = logRemoteMobile; + mAuthToken = authToken; + mEventDuration = eventDuration; + mAutoPruneDb = autoPruneDb; + mDataRetentionPeriod = dataRetentionPeriod; + mRemoteLogPeriod = remoteLogPeriod; Log.v(TAG, "mLogRemote=" + mLogRemote); - mLogRemoteMobile = (prefs.getBoolean("LogDataRemoteMobile", false)); Log.v(TAG, "mLogRemoteMobile=" + mLogRemoteMobile); - - prefVal = prefs.getString("EventDurationSec", "300"); - mEventDuration = Integer.parseInt(prefVal); Log.v(TAG, "mEventDuration=" + mEventDuration); - - mAutoPruneDb = prefs.getBoolean("AutoPruneDb", false); Log.v(TAG, "mAutoPruneDb=" + mAutoPruneDb); - - prefVal = prefs.getString("DataRetentionPeriod", "28"); - mDataRetentionPeriod = Integer.parseInt(prefVal); Log.v(TAG, "mDataRetentionPeriod=" + mDataRetentionPeriod); - - prefVal = prefs.getString("RemoteLogPeriod", "60"); - mRemoteLogPeriod = Integer.parseInt(prefVal); Log.v(TAG, "mRemoteLogPeriod=" + mRemoteLogPeriod); - mUtil = new OsdUtil(mContext, handler); openDb(); - mWac = new WebApiConnection(mContext, this, this, this); + mWac = new WebApiConnection(mContext); + mWac.setStoredToken(mAuthToken); startRemoteLogTimer(); @@ -417,8 +413,8 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface String whereClauseDate = "DataTime callback) { Log.v(TAG, "getNextEventToDate - dateStr=" + dateStr); - String[] columns = {"*","(julianday(dataTime)-julianday(datetime('" + dateStr + "'))) as ddiff"}; + String[] columns = {"*", "(julianday(dataTime)-julianday(datetime('" + dateStr + "'))) as ddiff"}; //SQLStr = "SELECT *, (julianday(dataTime)-julianday(datetime('" + dateStr + "'))) as ddiff from " + mDbTableName + " order by ABS(ddiff) asc;"; String orderByStr = "ABS(ddiff) asc"; new SelectQueryTask(mDbTableName, columns, null, null, @@ -601,24 +597,22 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface } else { whereClause = "Status in (?, ?, ?)"; } - return(whereClause); + return (whereClause); } private String[] getEventWhereArgs(boolean includeWarnings) { String[] whereArgs; if (includeWarnings) { - String[] whereArgsWarnings = { "1", "2", "3", "5"}; + String[] whereArgsWarnings = {"1", "2", "3", "5"}; whereArgs = whereArgsWarnings; } else { - String[] whereArgsNoWarnings = { "2", "3", "5"}; + String[] whereArgsNoWarnings = {"2", "3", "5"}; whereArgs = whereArgsNoWarnings; } - return(whereArgs); + return (whereArgs); } - - /*************************************************************************************** * Remote Database Part */ @@ -694,17 +688,13 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface Log.e(TAG, "Error parsing date " + eventDateStr); return; } - mWac.createEvent(eventAlarmStatus, eventDate, "Uploaded by OpenSeizureDetector Android App"); + mWac.createEvent(eventAlarmStatus, eventDate, "", this::createEventCallback); } else { Log.v(TAG, "UploadSdData - no data to upload"); } }); } - public void authCallback(boolean authSuccess, String tokenStr) { - Log.v(TAG, "authCallback"); - } - // Mark the relevant member variables to show we are not currently doing an upload, so a new one can be // started if necessary. @@ -717,7 +707,7 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface // Called by WebApiConnection when a new event record is created. // Once the event is created it queries the local database to find the datapoints associated with the event // and uploads those as a batch of data points. - public void eventCallback(boolean success, String eventStr) { + public void createEventCallback(String eventStr) { Log.v(TAG, "eventCallback(): " + eventStr); Date eventDate; String eventDateStr; @@ -788,7 +778,7 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface } Log.v(TAG, "uploadDatapoint() - uploading datapoint with local id of " + mCurrentDatapointId); - mWac.createDatapoint(mDatapointsToUploadList.get(0), mCurrentEventId); + mWac.createDatapoint(mDatapointsToUploadList.get(0), mCurrentEventId, this::datapointCallback); } else { mCurrentEventId = -1; @@ -800,7 +790,7 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface // Called by WebApiConnection when a new datapoint is created. It assumes that we have just created // a datapoint based on mDatapointsToUploadList(0) so removes that from the list and calls UploadDatapoint() // to upload the next one. - public void datapointCallback(boolean success, String datapointStr) { + public void datapointCallback(String datapointStr) { Log.v(TAG, "datapointCallback() " + datapointStr + ", mCurrentEventId=" + mCurrentEventId); mDatapointsToUploadList.remove(0); setDatapointToUploaded(mCurrentDatapointId, mCurrentEventId); diff --git a/app/src/main/java/uk/org/openseizuredetector/MainActivity.java b/app/src/main/java/uk/org/openseizuredetector/MainActivity.java index 05e55e4..7fdcfcd 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.Activity; import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; @@ -294,7 +292,7 @@ public class MainActivity extends AppCompatActivity { try { Intent i = new Intent( MainActivity.this, - DBQueryActivity.class); + ExportDataActivity.class); this.startActivity(i); } catch (Exception ex) { Log.i(TAG, "exception starting export activity " + ex.toString()); diff --git a/app/src/main/java/uk/org/openseizuredetector/RemoteDbActivity.java b/app/src/main/java/uk/org/openseizuredetector/RemoteDbActivity.java index cc5af03..439a0dc 100644 --- a/app/src/main/java/uk/org/openseizuredetector/RemoteDbActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/RemoteDbActivity.java @@ -4,8 +4,11 @@ package uk.org.openseizuredetector; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.CountDownTimer; +import android.os.Handler; +import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; @@ -22,6 +25,10 @@ public class RemoteDbActivity extends AppCompatActivity { private UiTimer mUiTimer; private LogManager mLm; private WebView mWebView; + private SdServiceConnection mConnection; + private OsdUtil mUtil; + final Handler serverStatusHandler = new Handler(); + private String TOKEN_ID = "webApiAuthToken"; @Override @@ -30,7 +37,12 @@ public class RemoteDbActivity extends AppCompatActivity { super.onCreate(savedInstanceState); mContext = this; setContentView(R.layout.activity_remote_db); - mLm= new LogManager(mContext); + mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler); + mConnection = new SdServiceConnection(getApplicationContext()); + mUtil.bindToServer(getApplicationContext(), mConnection); + waitForConnection(); + + //mLm= new LogManager(mContext); Button authBtn = (Button) findViewById(R.id.auth_button); @@ -43,12 +55,37 @@ public class RemoteDbActivity extends AppCompatActivity { WebSettings webSettings = mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); - updateUi(); } + private void waitForConnection() { + // We want the UI to update as soon as it is displayed, but it takes a finite time for + // the mConnection to bind to the service, so we delay half a second to give it chance + // to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer) + if (mConnection.mBound) { + Log.v(TAG, "waitForConnection - Bound!"); + initialiseServiceConnection(); + } else { + Log.v(TAG, "waitForConnection - waiting..."); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + waitForConnection(); + } + }, 100); + } + } + + private void initialiseServiceConnection() { + mLm = mConnection.mSdServer.mLm; + //mWac = mConnection.mSdServer.mLm.mWac; + } + + @Override protected void onStart() { super.onStart(); + waitForConnection(); + updateUi(); //startUiTimer(); } @@ -67,11 +104,16 @@ public class RemoteDbActivity extends AppCompatActivity { private HashMap getAuthHeaders() { HashMap headersMap = new HashMap<>(); - String authToken = mLm.mWac.getStoredToken(); + String authToken = getAuthToken(); headersMap.put("Authorization", "Token "+authToken); return (headersMap); } + public String getAuthToken() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + String authToken = prefs.getString(TOKEN_ID, null); + return authToken; + } private void updateUi() { Log.v(TAG,"updateUi()"); diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index d835586..43eceac 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -86,8 +86,9 @@ public class SdServer extends Service implements SdDataReceiver { // Notification ID private int NOTIFICATION_ID = 1; + private int EVENT_NOTIFICATION_ID = 2; private String mNotChId = "OSD Notification Channel"; - private CharSequence mNotChName = "OSD Notification Chennel"; + private CharSequence mNotChName = "OSD Notification Channel"; private String mNotChDesc = "OSD Notification Channel Description"; private NotificationManager mNM; @@ -130,9 +131,13 @@ public class SdServer extends Service implements SdDataReceiver { private boolean mLogData = false; private boolean mLogDataRemote = false; private boolean mLogDataRemoteMobile = false; - private String mOSDUname = ""; - private String mOSDPasswd = ""; - private int mOSDWearerId = 0; + private String mAuthToken = null; + 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 = 60; // Period in seconds between uploads to the remote server. + private long mAutoPrunePeriod = 3600; // Prune the database every hour + private boolean mAutoPruneDb; + private String mOSDUrl = ""; private OsdUtil mUtil; @@ -219,7 +224,8 @@ public class SdServer extends Service implements SdDataReceiver { updatePrefs(); // Create our log manager. - mLm = new LogManager(this); + mLm = new LogManager(this, mLogDataRemote, mLogDataRemoteMobile, mAuthToken, mEventDuration, + mRemoteLogPeriod, mAutoPruneDb, mDataRetentionPeriod); Log.v(TAG, "onStartCommand: Datasource =" + mSdDataSourceName); switch (mSdDataSourceName) { @@ -1197,12 +1203,32 @@ public class SdServer extends Service implements SdDataReceiver { mLogDataRemoteMobile = SP.getBoolean("LogDataRemoteMobile", false); Log.v(TAG, "updatePrefs() - mLogDataRemoteMobile = " + mLogDataRemoteMobile); mUtil.writeToSysLogFile("updatePrefs() - mLogDataRemoteMobile = " + mLogDataRemoteMobile); - mOSDUname = SP.getString("OSDUname", ""); - Log.v(TAG, "updatePrefs() - mOSDUname = " + mOSDUname); - mOSDPasswd = SP.getString("OSDPasswd", ""); - Log.v(TAG, "updatePrefs() - mOSDPasswd = " + mOSDPasswd); - mOSDWearerId = Integer.parseInt(SP.getString("OSDWearerId", "0")); - Log.v(TAG, "updatePrefs() - mOSDWearerId = " + mOSDWearerId); + mAuthToken = SP.getString("webApiAuthToken", null); + Log.v(TAG, "updatePrefs() - mAuthToken = " + mAuthToken); + mUtil.writeToSysLogFile("updatePrefs() - mAuthToken = " + mAuthToken); + + String prefVal; + prefVal = SP.getString("EventDurationSec", "300"); + mEventDuration = Integer.parseInt(prefVal); + Log.v(TAG, "mEventDuration=" + mEventDuration); + + mAutoPruneDb = SP.getBoolean("AutoPruneDb", false); + Log.v(TAG, "mAutoPruneDb=" + mAutoPruneDb); + + prefVal = SP.getString("DataRetentionPeriod", "28"); + mDataRetentionPeriod = Integer.parseInt(prefVal); + Log.v(TAG, "mDataRetentionPeriod=" + mDataRetentionPeriod); + + prefVal = SP.getString("RemoteLogPeriod", "60"); + mRemoteLogPeriod = Integer.parseInt(prefVal); + Log.v(TAG, "mRemoteLogPeriod=" + mRemoteLogPeriod); + + //mOSDUname = SP.getString("OSDUname", ""); + //Log.v(TAG, "updatePrefs() - mOSDUname = " + mOSDUname); + //mOSDPasswd = SP.getString("OSDPasswd", ""); + //Log.v(TAG, "updatePrefs() - mOSDPasswd = " + mOSDPasswd); + //mOSDWearerId = Integer.parseInt(SP.getString("OSDWearerId", "0")); + //Log.v(TAG, "updatePrefs() - mOSDWearerId = " + mOSDWearerId); mOSDUrl = SP.getString("OSDUrl", "http://openseizuredetector.org.uk/webApi"); Log.v(TAG, "updatePrefs() - mOSDUrl = " + mOSDUrl); mUtil.writeToSysLogFile( "updatePrefs() - mOSDUrl = " + mOSDUrl); diff --git a/app/src/main/java/uk/org/openseizuredetector/WebApiConnection.java b/app/src/main/java/uk/org/openseizuredetector/WebApiConnection.java index de46926..e3e6fcb 100644 --- a/app/src/main/java/uk/org/openseizuredetector/WebApiConnection.java +++ b/app/src/main/java/uk/org/openseizuredetector/WebApiConnection.java @@ -38,25 +38,26 @@ public class WebApiConnection { public int retCode; private String mUrlBase = "https://osdApi.ddns.net"; private String TAG = "WebApiConnection"; - private AuthCallbackInterface mAuthCallback; - private EventCallbackInterface mEventCallback; - private DatapointCallbackInterface mDatapointCallback; + private String mAuthToken; private Context mContext; - private String TOKEN_ID = "webApiAuthToken"; private OsdUtil mUtil; RequestQueue mQueue; - public WebApiConnection(Context context, AuthCallbackInterface authCallback, EventCallbackInterface eventCallback, - DatapointCallbackInterface datapointCallback) { + public WebApiConnection(Context context) { mContext = context; - mAuthCallback = authCallback; - mEventCallback = eventCallback; - mDatapointCallback = datapointCallback; mQueue = Volley.newRequestQueue(context); mUtil = new OsdUtil(mContext, new Handler()); } - public boolean authenticate(final String uname, final String passwd) { + /** + * Attempt to authenticate with the web API using user name uname and password passwd. Calls function callback with either + * the authentication token on success or null on failure. + * @param uname - user name + * @param passwd - password + * @param callback - call back function callback(String retVal) + * @return true if request sent, or false if failed to send request. + */ + public boolean authenticate(final String uname, final String passwd, Consumer callback) { // NOTE: the 'final' keyword is necessary for uname and passwd to be accessible to getParams below - I don't know why! // We know that this command works, so we just need the Java equivalent: // curl -X POST -d 'login=graham4&password=testpwd1' https://osdapi.ddns.net/api/accounts/login/ @@ -69,7 +70,7 @@ public class WebApiConnection { new Response.Listener() { @Override public void onResponse(String response) { - String tokenStr; + String tokenStr = null; Log.v(TAG, "Response is: " + response); try { JSONObject jo = new JSONObject(response); @@ -77,8 +78,8 @@ public class WebApiConnection { } catch (JSONException e) { tokenStr = "Error Parsing Rsponse"; } - saveStoredToken(tokenStr); - mAuthCallback.authCallback(true, response); + setStoredToken(tokenStr); + callback.accept(tokenStr); } }, new Response.ErrorListener() { @@ -90,8 +91,8 @@ public class WebApiConnection { } else { Log.e(TAG,"Login Error: Returned null response"); } - saveStoredToken(null); - mAuthCallback.authCallback(false, new String(error.networkResponse.data)); + setStoredToken(null); + callback.accept(null); } }) { // Note, this is overriding part of StringRequest, not one of the sub-classes above! @@ -112,20 +113,16 @@ public class WebApiConnection { // Remove the stored token so future calls are not authenticated. public void logout() { Log.v(TAG, "logout()"); - saveStoredToken(null); + setStoredToken(null); + //saveStoredToken(null); } - - private void saveStoredToken(String tokenStr) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); - prefs.edit().putString(TOKEN_ID, tokenStr).commit(); - + public void setStoredToken(String authToken) { + mAuthToken = authToken; } - public String getStoredToken() { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); - String authToken = prefs.getString(TOKEN_ID, null); - return authToken; + private String getStoredToken() { + return(mAuthToken); } public boolean isLoggedIn() { @@ -142,7 +139,7 @@ public class WebApiConnection { // Create a new event in the remote database, based on the provided parameters. - public boolean createEvent(final int osdAlarmState, final Date eventDate, final String eventDesc) { + public boolean createEvent(final int osdAlarmState, final Date eventDate, final String eventDesc, Consumer callback) { Log.v(TAG, "createEvent()"); String urlStr = mUrlBase + "/api/events/"; Log.v(TAG, "urlStr=" + urlStr); @@ -169,7 +166,7 @@ public class WebApiConnection { @Override public void onResponse(String response) { Log.v(TAG, "Response is: " + response); - mEventCallback.eventCallback(true, response); + callback.accept(response); } }, new Response.ErrorListener() { @@ -178,10 +175,10 @@ public class WebApiConnection { if (error != null) { String responseBody = new String(error.networkResponse.data); Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage() + ", Response Code:" + error.networkResponse.statusCode + ", Response: " + responseBody); - mEventCallback.eventCallback(false, new String(error.networkResponse.data)); + callback.accept(null); } else { Log.e(TAG,"Create Event Error - null respones"); - mEventCallback.eventCallback(false, null); + callback.accept(null); } } }) { @@ -428,7 +425,7 @@ public class WebApiConnection { - public boolean createDatapoint(JSONObject dataObj, int eventId) { + public boolean createDatapoint(JSONObject dataObj, int eventId, Consumercallback) { Log.v(TAG, "createDatapoint()"); // Create a new event in the remote database, based on the provided parameters. String urlStr = mUrlBase + "/api/datapoints/"; @@ -459,7 +456,7 @@ public class WebApiConnection { @Override public void onResponse(String response) { Log.v(TAG, "Response is: " + response); - mDatapointCallback.datapointCallback(true, response); + callback.accept(response); } }, new Response.ErrorListener() { @@ -469,10 +466,10 @@ public class WebApiConnection { // Fixme = are we sure that networResponse.data is not null??? String responseBody = new String(error.networkResponse.data); Log.e(TAG, "Create Datapoint Error: " + error.toString() + ", message:" + error.getMessage() + ", Response Code:" + error.networkResponse.statusCode + ", Response: " + responseBody); - mDatapointCallback.datapointCallback(false, new String(error.networkResponse.data)); + callback.accept(null); } else { Log.e(TAG,"Create Datapoint Error - returned null respones"); - mDatapointCallback.datapointCallback(false, null); + callback.accept(null); } } }) { @@ -484,9 +481,6 @@ public class WebApiConnection { String authToken = getStoredToken(); params.put("Authorization: Token " + authToken, authToken); Log.v(TAG, "getParams: params=" + params.toString()); - //params.put("eventType", String.valueOf(eventType)); - //params.put("dataTime", dateFormat.format(eventDate)); - //params.put("desc", eventDesc); return params; } diff --git a/app/src/main/res/drawable/stat_sample.png b/app/src/main/res/drawable/stat_sample.png deleted file mode 100644 index 1733042..0000000 Binary files a/app/src/main/res/drawable/stat_sample.png and /dev/null differ diff --git a/app/src/main/res/layout/event_sub_type_list_item.xml b/app/src/main/res/layout/event_sub_type_list_item.xml deleted file mode 100644 index 437e3c0..0000000 --- a/app/src/main/res/layout/event_sub_type_list_item.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/event_type_list_item.xml b/app/src/main/res/layout/event_type_list_item.xml deleted file mode 100644 index 68d2454..0000000 --- a/app/src/main/res/layout/event_type_list_item.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/raw/alarm.wav b/app/src/main/res/raw/alarm.wav deleted file mode 100644 index de8bab5..0000000 Binary files a/app/src/main/res/raw/alarm.wav and /dev/null differ diff --git a/app/src/main/res/raw/fault.mp3 b/app/src/main/res/raw/fault.mp3 deleted file mode 100644 index 86d7dea..0000000 Binary files a/app/src/main/res/raw/fault.mp3 and /dev/null differ diff --git a/app/src/main/res/raw/warning.wav b/app/src/main/res/raw/warning.wav deleted file mode 100644 index 2779a1d..0000000 Binary files a/app/src/main/res/raw/warning.wav and /dev/null differ diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f33fd20..715463a 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,6 +3,4 @@ 16dp 16dp - 16dp - 16dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 44f664f..5885930 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -7,11 +7,5 @@ @color/colorPrimaryDark @color/colorAccent - -