Merge branch 'Logging_firebase' into Logging

This commit is contained in:
Graham Jones
2022-04-10 21:23:51 +01:00
23 changed files with 1904 additions and 1017 deletions

1
.gitignore vendored
View File

@@ -7,5 +7,6 @@ app/release/app-release.apk
app/build
app/app.iml
app/release/output-metadata.json
app/google-services.json
*#

View File

@@ -1,5 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 31
useLibrary 'org.apache.http.legacy'
@@ -37,6 +37,8 @@ dependencies {
// Unit testing dependencies
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.android.material:material:1.4.0'
implementation 'com.google.firebase:firebase-auth:19.2.0'
implementation 'androidx.test:core:1.4.0'
testImplementation 'junit:junit:4.13.2'
// Set this dependency if you want to use Mockito
testImplementation 'org.mockito:mockito-core:4.3.1'
@@ -53,7 +55,10 @@ dependencies {
//implementation 'com.github.RohitSurwase.UCE-Handler:uce_handler:1.3'
testImplementation 'org.robolectric:robolectric:4.7.3'
implementation 'com.android.volley:volley:1.2.1'
implementation platform('com.google.firebase:firebase-bom:29.2.0')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.firebaseui:firebase-ui-auth:7.2.0'
implementation 'com.google.firebase:firebase-firestore'
}
repositories {

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="uk.org.openseizuredetector"
android:versionCode="99"
android:versionName="4.0.2">
android:versionCode="100"
android:versionName="4.1.1">
<!-- android:allowBackup="false" -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

View File

@@ -5,6 +5,9 @@ import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
@@ -15,19 +18,28 @@ import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.firebase.ui.auth.AuthUI;
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;
public class AuthenticateActivity extends AppCompatActivity {
private String TAG = "AuthenticateActivity";
private OsdUtil mUtil;
private EditText mUnameEt;
private EditText mPasswdEt;
private SdServiceConnection mConnection;
final Handler serverStatusHandler = new Handler();
private WebApiConnection mWac;
private LogManager mLm;
private SdServiceConnection mConnection;
private OsdUtil mUtil;
final Handler serverStatusHandler = new Handler();
private String TOKEN_ID = "webApiAuthToken";
private static final String TOKEN_ID = "webApiAuthToken";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -36,24 +48,28 @@ public class AuthenticateActivity extends AppCompatActivity {
setContentView(R.layout.activity_authenticate);
mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler);
if (!mUtil.isServerRunning()) {
mUtil.showToast(getString(R.string.error_server_not_running));
finish();
return;
}
mConnection = new SdServiceConnection(getApplicationContext());
Button cancelBtn =
(Button) findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel);
Button OKBtn = (Button) findViewById(R.id.OKBtn);
OKBtn.setOnClickListener(onOK);
Button loginBtn = (Button) findViewById(R.id.loginBtn);
loginBtn.setOnClickListener(onLogin);
Button logoutCancelBtn =
(Button) findViewById(R.id.logoutCancelBtn);
logoutCancelBtn.setOnClickListener(onCancel);
Button logoutBtn = (Button)findViewById(R.id.logoutBtn);
Button logoutBtn = (Button) findViewById(R.id.logoutBtn);
logoutBtn.setOnClickListener(onLogout);
// Components required only for osdapi backend
if (LogManager.USE_FIREBASE_BACKEND) { }
else {
mConnection = new SdServiceConnection(getApplicationContext());
Button registerBtn = (Button) findViewById(R.id.RegisterBtn);
registerBtn.setOnClickListener(onRegister);
Button resetPasswordBtn = (Button) findViewById(R.id.ResetPasswordBtn);
@@ -61,25 +77,59 @@ public class AuthenticateActivity extends AppCompatActivity {
mUnameEt = (EditText) findViewById(R.id.username);
mPasswdEt = (EditText) findViewById(R.id.password);
//mWac = new WebApiConnection(this, String tokenStr);
//mLm = new LogManager(this);
}
Button aboutDataSharingBtn = (Button) findViewById(R.id.aboutDataSharingBtn);
aboutDataSharingBtn.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG,"aboutDataSharingBtn.onClick()");
String url = OsdUtil.DATA_SHARING_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}
}
);
Button privacyPolicyBtn = (Button) findViewById(R.id.privacyPolicyBtn);
privacyPolicyBtn.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG,"privacyPolicyBtn.onClick()");
String url = OsdUtil.PRIVACY_POLICY_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}
}
);
}
@Override
protected void onStart() {
Log.d(TAG, "onStart()");
super.onStart();
if (LogManager.USE_FIREBASE_BACKEND) {
updateUi();
} else {
mUtil.bindToServer(getApplicationContext(), mConnection);
waitForConnection();
}
}
@Override
protected void onStop() {
Log.d(TAG, "onStop()");
super.onStop();
if (LogManager.USE_FIREBASE_BACKEND) {
} else {
mUtil.unbindFromServer(getApplicationContext(), mConnection);
}
}
private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for
@@ -100,40 +150,49 @@ public class AuthenticateActivity extends AppCompatActivity {
}
private void initialiseServiceConnection() {
Log.v(TAG,"initialiseServiceConnection()");
mLm = mConnection.mSdServer.mLm;
mWac = mConnection.mSdServer.mLm.mWac;
updateUi();
}
private void updateUi() {
SharedPreferences prefs;
String storedAuthToken;
LinearLayout loginLl = (LinearLayout)findViewById(R.id.login_ui);
LinearLayout logoutLl = (LinearLayout)findViewById(R.id.logout_ui);
Log.i(TAG, "switchUi()");
storedAuthToken = getAuthToken(); //mWac.getStoredToken();
//prefs = PreferenceManager.getDefaultSharedPreferences(this);
//storedAuthToken = (prefs.getString("webApiAuthToken", null));
Log.v(TAG, "storedAuthToken=" + storedAuthToken);
// Check if we are already logged in
if (storedAuthToken == null || storedAuthToken.length() == 0) {
Log.v(TAG, "Not Logged in - showing log in UI");
loginLl.setVisibility(View.VISIBLE);
logoutLl.setVisibility(View.GONE);
} else {
// Called after the Firebase Auth UI has completed
private ActivityResultLauncher<Intent> signInLauncher = registerForActivityResult(
new FirebaseAuthUIActivityResultContract(),
(result) -> {
Log.i(TAG, "FirebaseAuthUIActivityResult - " + result.toString());
updateUi();
});
// ...
private void updateUi() {
Log.v(TAG,"updateUi()");
LinearLayout loginLl = (LinearLayout) findViewById(R.id.login_ui);
LinearLayout osdApiLoginLl = (LinearLayout) findViewById(R.id.login_osdapi_ui);
LinearLayout logoutLl = (LinearLayout) findViewById(R.id.logout_ui);
if (mWac == null) {
Log.i(TAG,"mWac is null - not updating UI");
return;
}
if (mWac.isLoggedIn()) {
Log.v(TAG, "Already Logged in - showing Log Out prompt");
loginLl.setVisibility(View.GONE);
logoutLl.setVisibility(View.VISIBLE);
//TextView tv = (TextView)findViewById(R.id.tokenTv);
//tv.setText("Logged in with Token: "+storedAuthToken);
if (mWac != null) {
if (!LogManager.USE_FIREBASE_BACKEND) {
osdApiLoginLl.setVisibility(View.GONE);
}
mWac.getUserProfile((JSONObject profileObj) -> {
try {
Long userId = profileObj.getLong("id");
String userId = profileObj.getString("id");
String userName = profileObj.getString("username");
TextView tv2 = (TextView) findViewById(R.id.userIdTv);
tv2.setText(userId.toString());
tv2.setText(userId);
tv2 = (TextView) findViewById(R.id.usernameTv);
tv2.setText(userName);
} catch (JSONException e) {
@@ -142,11 +201,15 @@ public class AuthenticateActivity extends AppCompatActivity {
}
});
} else {
Log.i(TAG,"UpdateUI - not retrieving profile because mWac is null");
}
Log.v(TAG,"updateUi() - not logged in..");
loginLl.setVisibility(View.VISIBLE);
logoutLl.setVisibility(View.GONE);
if (!LogManager.USE_FIREBASE_BACKEND) {
osdApiLoginLl.setVisibility(View.VISIBLE);
}
}
}
View.OnClickListener onCancel =
new View.OnClickListener() {
@@ -158,12 +221,32 @@ public class AuthenticateActivity extends AppCompatActivity {
}
};
View.OnClickListener onOK =
View.OnClickListener onLogin =
new View.OnClickListener() {
@Override
public void onClick(View view) {
//m_status=true;
Log.v(TAG, "onOK()");
if (LogManager.USE_FIREBASE_BACKEND) {
Log.v(TAG, "onLogin() - using Firebase Login");
Intent signInIntent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(Arrays.asList(
new AuthUI.IdpConfig.GoogleBuilder().build(),
//new AuthUI.IdpConfig.FacebookBuilder().build(),
//new AuthUI.IdpConfig.TwitterBuilder().build(),
//new AuthUI.IdpConfig.MicrosoftBuilder().build(),
//new AuthUI.IdpConfig.YahooBuilder().build(),
//new AuthUI.IdpConfig.AppleBuilder().build(),
new AuthUI.IdpConfig.EmailBuilder().build()
//new AuthUI.IdpConfig.PhoneBuilder().build()
//new AuthUI.IdpConfig.AnonymousBuilder().build()))
))
// ... options ...
.build();
signInLauncher.launch(signInIntent);
} else {
// Use Username and password authentication for OSDAPI.
// FIXME - make this work with Google Authentication like we do for Firebase.
String uname = mUnameEt.getText().toString();
String passwd = mPasswdEt.getText().toString();
Log.v(TAG,"onOK() - uname="+uname+", passwd="+passwd);
@@ -182,18 +265,31 @@ public class AuthenticateActivity extends AppCompatActivity {
}
}
});
//finish();
}
}
};
View.OnClickListener onLogout =
new View.OnClickListener() {
View.OnClickListener onLogout = new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onLogout");
//m_status=false;
if (LogManager.USE_FIREBASE_BACKEND) {
AuthUI.getInstance()
.signOut(getApplicationContext())
.addOnCompleteListener(new OnCompleteListener<Void>() {
public void onComplete(@NonNull Task<Void> task) {
// user is now signed out
updateUi();
}
});
} else {
if (mWac != null) {
mWac.logout();
saveAuthToken(null);
} else {
Log.e(TAG,"logout() - mWac is null - not doing anything");
}
}
updateUi();
}
};
@@ -244,5 +340,4 @@ public class AuthenticateActivity extends AppCompatActivity {
}
}

View File

@@ -35,7 +35,7 @@ public class EditEventActivity extends AppCompatActivity {
private HashMap<String, ArrayList<String>> mEventSubTypesHashMap = null;
private String mEventTypeStr = null;
private String mEventSubTypeStr = null;
private Long mEventId;
private String mEventId;
private String mEventNotes = "";
//private Date mEventDateTime;
private RadioGroup mEventTypeRg;
@@ -59,7 +59,7 @@ public class EditEventActivity extends AppCompatActivity {
Bundle extras = getIntent().getExtras();
if (extras != null) {
Long eventId = extras.getLong("eventId");
String eventId = extras.getString("eventId");
mEventId = eventId;
Log.v(TAG, "onCreate - mEventId=" + mEventId);
}
@@ -68,7 +68,7 @@ public class EditEventActivity extends AppCompatActivity {
Button cancelBtn =
(Button) findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel);
Button OKBtn = (Button) findViewById(R.id.OKBtn);
Button OKBtn = (Button) findViewById(R.id.loginBtn);
OKBtn.setOnClickListener(onOK);
mEventTypeRg = findViewById(R.id.eventTypeRg);
@@ -159,10 +159,10 @@ public class EditEventActivity extends AppCompatActivity {
mWac.getEvent(mEventId, new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventObj) {
Log.v(TAG, "onCreate.getEvent");
Log.v(TAG, "initialiseServiceConnection.getEvent");
if (eventObj != null) {
mEventObj = eventObj;
Log.v(TAG, "onCreate.getEvent: eventObj=" + eventObj.toString());
Log.v(TAG, "initialiseServiceConnection.getEvent: eventObj=" + eventObj.toString());
updateUi();
// FIXME: modify updateUi to use mEventObj
} else {
@@ -198,20 +198,27 @@ public class EditEventActivity extends AppCompatActivity {
try {
if (mEventObj != null) {
tv = (TextView) findViewById(R.id.eventIdTv);
tv.setText(String.valueOf(mEventObj.getLong("id")));
tv.setText(mEventId);
tv = (TextView) findViewById(R.id.eventAlarmStateTv);
tv.setText(mEventObj.getString("alarmStateStr"));
String alarmStateStr = mEventObj.getString("osdAlarmState");
try {
int alarmStateVal = Integer.parseInt(alarmStateStr);
alarmStateStr = mUtil.alarmStatusToString(alarmStateVal);
} catch (Exception e) {
Log.v(TAG,"updateUi: alarmState does not parse to int so displaying it as string: " +alarmStateStr);
}
tv.setText(alarmStateStr);
tv = (TextView) findViewById(R.id.eventNotsTv);
tv.setText(mEventObj.getString("desc"));
tv = (TextView) findViewById(R.id.eventDateTv);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date dataTime = dateFormat.parse(mEventObj.getString("dataTime"));
dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = mEventObj.getString("dataTime");
Date dataTime = mUtil.string2date(dateStr);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
tv.setText(dateFormat.format(dataTime));
} catch (ParseException e) {
} catch (Exception e) {
Log.e(TAG,"updateUI: Error Parsing dataDate "+e.getLocalizedMessage());
tv.setText("---");
}
@@ -283,12 +290,12 @@ public class EditEventActivity extends AppCompatActivity {
TextView tv = (TextView)findViewById(R.id.eventNotsTv);
try {
mEventObj.put("desc",tv.getText());
mEventObj.put("id",mEventId); // Add event Id to event object manually because firestore does not include it by default.
} catch (JSONException e) {
Log.e(TAG,"Error writing mEventObj: "+e.getMessage());
}
Log.v(TAG, "onOK() - eventObj="+mEventObj.toString());
try {
mWac.updateEvent(mEventObj, new WebApiConnection.JSONObjectCallback() {
@Override
@@ -307,7 +314,7 @@ public class EditEventActivity extends AppCompatActivity {
}
});
} catch (Exception e) {
Log.e(TAG,"ERROR:"+e.getMessage());
Log.e(TAG,"onOK() - ERROR: "+e.getMessage()+" : " +e.toString());
e.printStackTrace();
mUtil.showToast("Error Updating Event");
updateUi();

View File

@@ -68,6 +68,7 @@ public class LogManager {
static final private String TAG = "LogManager";
//private String mDbName = "osdData";
final static private String mDpTableName = "datapoints";
final static private String mEventsTableName = "events";
private boolean mLogRemote;
private boolean mLogRemoteMobile;
private String mAuthToken;
@@ -76,13 +77,15 @@ public class LogManager {
private static Context mContext;
private OsdUtil mUtil;
public static WebApiConnection mWac;
public static final boolean USE_FIREBASE_BACKEND = false;
private boolean mUploadInProgress;
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 ArrayList<JSONObject> mDatapointsToUploadList;
private int mCurrentEventId;
private String mCurrentEventRemoteId;
private long mCurrentEventLocalId = -1;
private int mCurrentDatapointId;
private long mAutoPrunePeriod = 3600; // Prune the database every hour
private boolean mAutoPruneDb;
@@ -121,7 +124,12 @@ public class LogManager {
mUtil = new OsdUtil(mContext, handler);
openDb();
Log.i(TAG, "Starting Remote Database Interface");
mWac = new WebApiConnection(mContext);
if (USE_FIREBASE_BACKEND) {
mWac = new WebApiConnection_firebase(mContext);
} else {
mWac = new WebApiConnection_osdapi(mContext);
}
mWac.setStoredToken(mAuthToken);
if (mLogRemote) {
@@ -162,7 +170,7 @@ public class LogManager {
try {
datapoint.put("id", c.getString(c.getColumnIndex("id")));
datapoint.put("dataTime", c.getString(c.getColumnIndex("dataTime")));
datapoint.put("status", c.getString(c.getColumnIndex("Status")));
datapoint.put("status", c.getString(c.getColumnIndex("status")));
datapoint.put("dataJSON", c.getString(c.getColumnIndex("dataJSON")));
datapoint.put("uploaded", c.getString(c.getColumnIndex("uploaded")));
//Log.v(TAG,"cursor2json() - datapoint="+datapoint.toString());
@@ -177,6 +185,41 @@ public class LogManager {
return dataPointArray.toString();
}
/**
* Returns a JSON String representing an array of events that are selected from sqlite cursor c.
*
* @param c sqlite cursor pointing to events query result.
* @return JSON String.
* from https://stackoverflow.com/a/20488153/2104584
*/
private String eventCursor2Json(Cursor c) {
StringBuilder cNames = new StringBuilder();
for (String n : c.getColumnNames()) {
cNames.append(", ").append(n);
}
c.moveToFirst();
Log.v(TAG, "eventCursor2Json: size of cursor=" + c.getCount());
JSONArray eventsArray = new JSONArray();
int i = 0;
while (!c.isAfterLast()) {
JSONObject event = new JSONObject();
try {
event.put("id", c.getString(c.getColumnIndex("id")));
event.put("dataTime", c.getString(c.getColumnIndex("dataTime")));
event.put("status", c.getString(c.getColumnIndex("status")));
event.put("uploaded", c.getString(c.getColumnIndex("uploaded")));
c.moveToNext();
eventsArray.put(i, event);
i++;
} catch (JSONException e) {
Log.e(TAG, "eventCursor2Json(): error creating JSON Object");
e.printStackTrace();
}
}
Log.v(TAG, "eventCursor2JSON(): returning " + eventsArray.toString());
return eventsArray.toString();
}
private static boolean openDb() {
Log.d(TAG, "openDb");
@@ -187,11 +230,14 @@ public class LogManager {
} else {
Log.i(TAG, "openDb: mOsdDb has been initialised already so not doing anything");
}
if (!checkTableExists(mOsdDb, mDpTableName)) {
Log.e(TAG, "ERROR - Table " + mDpTableName + " does not exist");
String[] tableNames = new String[]{mDpTableName, mEventsTableName};
for (String tableName : tableNames) {
if (!checkTableExists(mOsdDb, tableName)) {
Log.e(TAG, "ERROR - Table " + tableName + " does not exist");
return false;
} else {
Log.d(TAG, "table " + mDpTableName + " exists ok");
Log.d(TAG, "table " + tableName + " exists ok");
}
}
} catch (SQLException e) {
Log.e(TAG, "Failed to open Database: " + e.toString());
@@ -221,17 +267,19 @@ public class LogManager {
* FIXME - I am sure we should not be using raw SQL Srings to do this!
*/
public void writeDatapointToLocalDb(SdData sdData) {
Log.v(TAG, "writeDatapointToLocalDb()");
//Log.v(TAG, "writeDatapointToLocalDb()");
Date curDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = dateFormat.format(curDate);
String SQLStr = "SQLStr";
if (mOsdDb == null) {
Log.e(TAG, "writeDatapointToLocalDb(): mOsdDb is null - doing nothing");
return;
}
try {
//double roiRatio = -1;
//if (sdData.specPower != 0)
// roiRatio = 10. * sdData.roiPower / sdData.specPower;
// Write Datapoint to database
SQLStr = "INSERT INTO " + mDpTableName
+ "(dataTime, status, dataJSON, uploaded)"
+ " VALUES("
@@ -240,20 +288,55 @@ public class LogManager {
+ DatabaseUtils.sqlEscapeString(sdData.toJSON(true)) + ","
+ 0
+ ")";
if (mOsdDb != null) {
mOsdDb.execSQL(SQLStr);
Log.v(TAG, "writeDatapointToLocalDb(): data written to database");
} else {
Log.e(TAG,"writeDatapointToLocalDb(): mOsdDb is null");
}
Log.v(TAG, "writeDatapointToLocalDb(): datapoint written to database");
if (sdData.alarmState != 0) {
Log.i(TAG, "writeDatapointToLocalDb(): adding event to local DB");
createLocalEvent(dateStr, sdData.alarmState);
}
} catch (SQLException e) {
Log.e(TAG, "writeToLocalDb(): Error Writing Data: " + e.toString());
Log.e(TAG, "SQLStr was " + SQLStr);
} catch (NullPointerException e) {
Log.e(TAG, "writeToLocalDb(): Null Pointer Exception: " + e.toString());
}
}
public boolean createLocalEvent(String dataTime, long status) {
// Expects dataTime to be in format: SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.d(TAG,"createLocalEvent() - dataTime="+dataTime+", status="+status);
// Write Datapoint to database
String SQLStr = "INSERT INTO " + mEventsTableName
+ "(dataTime, status)"
+ " VALUES("
+ "'" + dataTime + "',"
+ status
+ ")";
mOsdDb.execSQL(SQLStr);
return true;
}
/**
* Returns a json representation of locally stored event 'id'.
*
* @param id event id to return
* @return JSON representation of requested event (single element JSON array)
*/
public String getLocalEventById(long id) {
Log.d(TAG, "getLocalEventById() - id=" + id);
Cursor c;
String retVal;
try {
String selectStr = "select * from " + mEventsTableName + " where id=" + id + ";";
c = mOsdDb.rawQuery(selectStr, null);
retVal = eventCursor2Json(c);
} catch (Exception e) {
Log.d(TAG, "getLocalEventById(): Error Querying Database: " + e.getLocalizedMessage());
retVal = null;
}
Log.d(TAG, "getLocalEventById() - returning " + retVal);
return (retVal);
}
@@ -269,9 +352,6 @@ public class LogManager {
String retVal;
try {
String selectStr = "select * from " + mDpTableName + " where id=" + id + ";";
//String[] selectArgs = new String[]{String.format("%d", id)};
//c = mOSDDb.getWritableDatabase().query(mDbTableName, null,
// selectStr, selectArgs, null, null, null);
c = mOsdDb.rawQuery(selectStr, null);
retVal = cursor2Json(c);
} catch (Exception e) {
@@ -279,7 +359,6 @@ public class LogManager {
retVal = null;
}
return (retVal);
}
/**
@@ -289,15 +368,17 @@ public class LogManager {
* @param eventId - the eventId associated with the uploaded datapoint - the 'uploaded' field is set to this value.
* @return True on success or False on failure.
*/
public boolean setDatapointToUploaded(int id, int eventId) {
public boolean setDatapointToUploaded(int id, String eventId) {
Log.d(TAG, "setDatapointToUploaded() - id=" + id);
if (mOsdDb == null) {
Log.e(TAG,"setDatapointToUploaded() - mOsdDb is null - not doing anything");
return false;
}
ContentValues cv = new ContentValues();
cv.put("uploaded", eventId);
int nRowsUpdated = mOsdDb.update(mDpTableName, cv, "id = ?",
new String[]{String.format("%d", id)});
return (nRowsUpdated == 1);
}
/**
@@ -356,7 +437,7 @@ public class LogManager {
String whereClause = getEventWhereClause(includeWarnings);
//sqlStr = "SELECT * from " + mDbTableName + " where Status in (" + statusListStr + ") order by dataTime desc;";
String[] columns = {"*"};
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs,
new SelectQueryTask(mEventsTableName, columns, whereClause, whereArgs,
null, null, "dataTime DESC", (Cursor cursor) -> {
Log.v(TAG, "getEventsList - returned " + cursor);
if (cursor != null) {
@@ -365,7 +446,7 @@ public class LogManager {
HashMap<String, String> event = new HashMap<>();
//event.put("id", cursor.getString(cursor.getColumnIndex("id")));
event.put("dataTime", cursor.getString(cursor.getColumnIndex("dataTime")));
int status = cursor.getInt(cursor.getColumnIndex("Status"));
int status = cursor.getInt(cursor.getColumnIndex("status"));
String statusStr = mUtil.alarmStatusToString(status);
event.put("status", statusStr);
event.put("uploaded", cursor.getString(cursor.getColumnIndex("uploaded")));
@@ -385,25 +466,48 @@ public class LogManager {
*/
public int pruneLocalDb() {
Log.d(TAG, "pruneLocalDb()");
int retVal;
int retVal = 0;
long currentDateMillis = new Date().getTime();
long endDateMillis = currentDateMillis - 24 * 3600 * 1000 * mDataRetentionPeriod;
//long endDateMillis = currentDateMillis - 3600*1000* mDataRetentionPeriod; // Using hours rather than days for testing
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endDateStr = dateFormat.format(new Date(endDateMillis));
String[] tableNames = new String[]{mDpTableName, mEventsTableName};
for (String tableName : tableNames) {
Log.i(TAG, "pruneLocalDb - pruning table " + tableName);
try {
String selectStr = "DataTime<=?";
String[] selectArgs = {endDateStr};
retVal = mOsdDb.delete(mDpTableName, selectStr, selectArgs);
retVal = mOsdDb.delete(tableName, selectStr, selectArgs);
} catch (Exception e) {
Log.d(TAG, "Error deleting datapoints" + e.toString());
Log.d(TAG, "Error deleting data " + e.toString());
retVal = 0;
}
Log.d(TAG, String.format("pruneLocalDb() - deleted %d records", retVal));
Log.d(TAG, String.format("pruneLocalDb() - deleted %d records from table %s", retVal, tableName));
}
return (retVal);
}
/**
* setEventToUploaded
*
* @param localEventId - local Event ID to change
* @param remoteEventId - the remote eventId associated with the uploaded datapoint - the 'uploaded' field is set to this value.
* @return True on success or False on failure.
*/
public boolean setEventToUploaded(long localEventId, String remoteEventId) {
Log.d(TAG, "setEventToUploaded() - local id=" + localEventId + " remote id="+remoteEventId);
if (mOsdDb == null) {
Log.e(TAG,"setEventToUploaded() - mOsdDb is null - not doing anything");
return false;
}
ContentValues cv = new ContentValues();
cv.put("uploaded", remoteEventId);
int nRowsUpdated = mOsdDb.update(mEventsTableName, cv, "id = ?",
new String[]{String.format("%d", localEventId)});
return (nRowsUpdated == 1);
}
/**
* Return the ID of the next event (alarm, warning, fall etc that needs to be uploaded (alarm or warning condition and has not yet been uploaded.
@@ -423,7 +527,7 @@ public class LogManager {
long endDateMillis = currentDateMillis - 1000 * mEventDuration;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endDateStr = dateFormat.format(new Date(endDateMillis));
String whereClauseUploaded = "uploaded = 0";
String whereClauseUploaded = "uploaded is null";
String whereClauseDate = "DataTime<?";
String whereClause = whereClauseStatus + " AND " + whereClauseUploaded + " AND " + whereClauseDate;
@@ -432,9 +536,8 @@ public class LogManager {
whereArgs[i] = whereArgsStatus[i];
}
whereArgs[whereArgsStatus.length] = endDateStr;
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs,
new SelectQueryTask(mEventsTableName, columns, whereClause, whereArgs,
null, null, "dataTime DESC", (Cursor cursor) -> {
Log.v(TAG, "getEventsList - returned " + cursor);
Long recordId = new Long(-1);
if (cursor != null) {
Log.v(TAG, "getNextEventToUpload - returned " + cursor.getCount() + " records");
@@ -443,9 +546,8 @@ public class LogManager {
Log.v(TAG, "getNextEventToUpload() - no events to Upload - exiting");
recordId = new Long(-1);
} else {
String recordStr = cursor.getString(3);
recordId = cursor.getLong(0);
Log.d(TAG, "getNextEventToUpload(): id=" + recordId + ", recordStr=" + recordStr);
Log.d(TAG, "getNextEventToUpload(): id=" + recordId);
}
}
callback.accept(recordId);
@@ -494,13 +596,13 @@ public class LogManager {
* @return True on successful start or false if call fails.
*/
public boolean getLocalEventsCount(boolean includeWarnings, WebApiConnection.LongCallback callback) {
Log.v(TAG, "getLocalEventsCount- includeWarnings=" + includeWarnings);
//Log.v(TAG, "getLocalEventsCount- includeWarnings=" + includeWarnings);
String[] whereArgs = getEventWhereArgs(includeWarnings);
String whereClause = getEventWhereClause(includeWarnings);
String[] columns = {"*"};
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs,
new SelectQueryTask(mEventsTableName, columns, whereClause, whereArgs,
null, null, null, (Cursor cursor) -> {
Log.v(TAG, "getLocalEventsCount - returned " + cursor);
//Log.v(TAG, "getLocalEventsCount - returned " + cursor);
Long eventCount = Long.valueOf(0);
if (cursor != null) {
eventCount = Long.valueOf(cursor.getCount());
@@ -517,13 +619,13 @@ public class LogManager {
* @return True on successful start or false if call fails.
*/
public boolean getLocalDatapointsCount(WebApiConnection.LongCallback callback) {
Log.v(TAG, "getLocalDatapointsCount");
//Log.v(TAG, "getLocalDatapointsCount");
String[] whereArgs = null;
String whereClause = null;
String[] columns = {"*"};
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs,
null, null, null, (Cursor cursor) -> {
Log.v(TAG, "getLocalDatapointsCount - returned " + cursor);
//Log.v(TAG, "getLocalDatapointsCount - returned " + cursor);
Long eventCount = Long.valueOf(0);
if (cursor != null) {
eventCount = Long.valueOf(cursor.getCount());
@@ -568,7 +670,7 @@ public class LogManager {
@Override
protected Cursor doInBackground(Void... params) {
Log.v(TAG, "runSelect.doInBackground()");
//Log.v(TAG, "runSelect.doInBackground()");
Log.v(TAG, "SelectQueryTask.doInBackground: mTable=" + mTable + ", mColumns=" + Arrays.toString(mColumns)
+ ", mSelection=" + mSelection + ", mSelectionArgs=" + Arrays.toString(mSelectionArgs) + ", mGroupBy=" + mGroupBy
+ ", mHaving =" + mHaving + ", mOrderBy=" + mOrderBy);
@@ -659,17 +761,24 @@ public class LogManager {
*/
public void uploadSdData() {
//int eventId = -1;
Log.v(TAG, "uploadSdData()");
//Log.v(TAG, "uploadSdData()");
// First try uploading full alarms, and only if we do not have any of those, upload warnings.
boolean warningsArr[] = { false, true };
for (int n=0; n<warningsArr.length; n++) {
//boolean warningsArr[] = {false, true};
// Upload everything - alarms and warnings - we can sort it out in post-processing the data!
boolean warningsArr[] = {true};
for (int n = 0; n < warningsArr.length; n++) {
boolean warningsVal = warningsArr[n];
Log.i(TAG, "uploadSdData(): warningsVal=" + warningsVal);
if (mUploadInProgress) {
Log.d(TAG, "uploadSdData - upload already in progress - not doing anything");
return;
}
mUploadInProgress = true;
getNextEventToUpload(warningsVal, (Long eventId) -> {
if (eventId != -1) {
Log.v(TAG, "uploadSdData() - eventId=" + eventId);
String eventJsonStr = getDatapointById(eventId);
Log.v(TAG, "uploadSdData() - eventJsonStr=" + eventJsonStr);
Log.i(TAG, "uploadSdData() - next Event to Upload eventId=" + eventId);
String eventJsonStr = getLocalEventById(eventId);
Log.v(TAG, "uploadSdData() - event to upload eventJsonStr=" + eventJsonStr);
//int eventType;
JSONObject eventObj;
int eventAlarmStatus;
@@ -680,36 +789,42 @@ public class LogManager {
eventObj = datapointJsonArr.getJSONObject(0); // We only look at the first (and hopefully only) item in the array.
eventAlarmStatus = Integer.parseInt(eventObj.getString("status"));
eventDateStr = eventObj.getString("dataTime");
Log.v(TAG, "uploadSdData - data from local DB is:" + eventJsonStr + ", eventAlarmStatus="
Log.d(TAG, "uploadSdData - data from local DB is:" + eventJsonStr + ", eventAlarmStatus="
+ eventAlarmStatus + ", eventDateStr=" + eventDateStr);
} catch (JSONException e) {
Log.e(TAG, "ERROR parsing event JSON Data" + eventJsonStr);
Log.e(TAG, "uploadSdData(): ERROR parsing event JSON Data" + eventJsonStr);
e.printStackTrace();
return;
} catch (NullPointerException e) {
Log.e(TAG, "ERROR null pointer exception parsing event JSON Data" + eventJsonStr);
Log.e(TAG, "uploadSdData(): ERROR null pointer exception parsing event JSON Data" + eventJsonStr);
e.printStackTrace();
return;
}
try {
eventDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(eventDateStr);
} catch (ParseException e) {
Log.e(TAG, "Error parsing date " + eventDateStr);
Log.e(TAG, "UploadSdData(): Error parsing date " + eventDateStr);
return;
}
Log.i(TAG, "uploadSdData - calling mWac.createEvent");
mCurrentEventLocalId = eventId;
mWac.createEvent(eventAlarmStatus, eventDate, "", this::createEventCallback);
} else {
Log.v(TAG, "UploadSdData - no data to upload");
Log.v(TAG, "uploadSdData - no data to upload "); //(warnings="+warningsVal+")");
mUploadInProgress = false;
}
});
}
}
// Mark the relevant member variables to show we are not currently doing an upload, so a new one can be
// Mark the relevant member variables to show we are not cuurrently doing an upload, so a new one can be
// started if necessary.
public void finishUpload() {
mCurrentEventId = -1;
mCurrentEventRemoteId = null;
mCurrentEventLocalId = -1;
mCurrentDatapointId = -1;
mDatapointsToUploadList = null;
mUploadInProgress = false;
}
@@ -717,82 +832,97 @@ public class LogManager {
// 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 createEventCallback(String eventStr) {
Log.v(TAG, "eventCallback(): " + eventStr);
public void createEventCallback(String eventId) {
Log.v(TAG, "createEventCallback(): " + eventId);
Log.v(TAG, "createEventCallback(): Retrieving remote event details");
mWac.getEvent(eventId, new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventObj) {
if (eventObj == null) {
Log.e(TAG,"createEventCallback() - eventObj is null - failed to create event");
mUtil.showToast("Error Creating Remote Event");
} else {
Log.v(TAG, "createEventCallback() - eventObj=" + eventObj.toString());
Date eventDate;
String eventDateStr;
int eventId;
String eventDateStr = "";
try {
JSONObject eventObj = new JSONObject(eventStr);
eventDateStr = eventObj.getString("dataTime");
eventId = eventObj.getInt("id");
String dateStr= eventObj.getString("dataTime");
eventDate = mUtil.string2date(dateStr);
} catch (JSONException e) {
Log.e(TAG, "eventCallback() - Error parsing eventStr: " + eventStr);
Log.e(TAG, "createEventCallback() - Error parsing JSONObject: " + eventObj.toString());
finishUpload();
return;
}
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
eventDate = dateFormat.parse(eventDateStr);
} catch (ParseException e) {
Log.e(TAG, "eventCallback() - error parsing date string " + eventDateStr);
finishUpload();
return;
}
Log.v(TAG, "eventCallback() EventId=" + eventId + ", eventDateStr=" + eventDateStr + ", eventDate=" + eventDate);
if (eventDate != null) {
Log.v(TAG, "createEventCallback() EventId=" + eventId + ", eventDateStr=" + eventDateStr + ", eventDate=" + eventDate);
mUploadInProgress = true;
long eventDateMillis = eventDate.getTime();
long startDateMillis = eventDateMillis - 1000 * mEventDuration / 2;
long endDateMillis = eventDateMillis + 1000 * mEventDuration / 2;
dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
getDatapointsByDate(
dateFormat.format(new Date(startDateMillis)),
dateFormat.format(new Date(endDateMillis)), (String datapointsJsonStr) -> {
Log.v(TAG, "eventCallback() - datapointsJsonStr=" + datapointsJsonStr);
dateFormat.format(new Date(endDateMillis)),
(String datapointsJsonStr) -> {
//Log.v(TAG, "createEventCallback() - datapointsJsonStr=" + datapointsJsonStr);
JSONArray dataObj;
mDatapointsToUploadList = new ArrayList<JSONObject>();
try {
//DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dataObj = new JSONArray(datapointsJsonStr);
Log.v(TAG, "createEventCallback() - datapointsObj length=" + dataObj.length());
for (int i = 0; i < dataObj.length(); i++) {
mDatapointsToUploadList.add(dataObj.getJSONObject(i));
}
} catch (JSONException e) {
Log.v(TAG, "Error Creating JSON Object from string " + datapointsJsonStr);
Log.v(TAG, "createEventCallback(): Error Creating JSON Object from string " + datapointsJsonStr);
dataObj = null;
finishUpload();
}
// This starts the process of uploading the datapoints, one at a time.
mCurrentEventId = eventId;
mUploadInProgress = true;
Log.v(TAG, "eventCallback() - starting datapoints upload with eventId " + mCurrentEventId);
mCurrentEventRemoteId = eventId;
Log.v(TAG, "createEventCallback() - starting datapoints upload with eventId " + mCurrentEventRemoteId +
" Uploading " + mDatapointsToUploadList.size() + " datapoints");
uploadNextDatapoint();
});
} else {
Log.e(TAG,"createEventCallback() - Error - event date is null - not doing anything");
mUtil.showToast("Error uploading event - date is null");
finishUpload();
}
}
}
});
}
// takes the next datapoint of the list mDatapointsToUploadList and uploads it to the remote server.
// datapointCallback is called when the upload is complete.
public void uploadNextDatapoint() {
Log.v(TAG, "uploadDatapoint()");
//Log.v(TAG, "uploadNextDatapoint()");
if (mDatapointsToUploadList != null) {
if (mDatapointsToUploadList.size() > 0) {
mUploadInProgress = true;
try {
mCurrentDatapointId = mDatapointsToUploadList.get(0).getInt("id");
} catch (JSONException e) {
Log.e(TAG, "Error reading currentDatapointID from mDatapointsToUploadList[0]" + e.getMessage());
Log.e(TAG, "Removing mDatapointsToUploadList[0] and trying the next datapoint");
Log.e(TAG, "uploadNextDatapoint(): Error reading currentDatapointID from mDatapointsToUploadList[0]" + e.getMessage());
Log.e(TAG, "uploadNextDatapoint(): Removing mDatapointsToUploadList[0] and trying the next datapoint");
mDatapointsToUploadList.remove(0);
uploadNextDatapoint();
}
Log.v(TAG, "uploadDatapoint() - uploading datapoint with local id of " + mCurrentDatapointId);
mWac.createDatapoint(mDatapointsToUploadList.get(0), mCurrentEventId, this::datapointCallback);
Log.v(TAG, "uploadNextDatapoint() - " + mDatapointsToUploadList.size() + " datapoints to upload. Uploading datapoint ID:" + mCurrentDatapointId);
mWac.createDatapoint(mDatapointsToUploadList.get(0), mCurrentEventRemoteId, this::datapointCallback);
} else {
mCurrentEventId = -1;
mCurrentDatapointId = -1;
mUploadInProgress = false;
Log.i(TAG, "uploadNextDatapoint() - All datapoints uploaded!");
setEventToUploaded(mCurrentEventLocalId, mCurrentEventRemoteId);
finishUpload();
}
} else {
Log.w(TAG,"uploadNextDatapoint - mDatapointsToUploadList is null - I don't thin this should have happened!");
}
}
@@ -800,11 +930,15 @@ public class LogManager {
// a datapoint based on mDatapointsToUploadList(0) so removes that from the list and calls UploadDatapoint()
// to upload the next one.
public void datapointCallback(String datapointStr) {
Log.v(TAG, "datapointCallback() " + datapointStr + ", mCurrentEventId=" + mCurrentEventId);
Log.v(TAG, "datapointCallback() dataPointId="+mCurrentDatapointId+" remote datapointID=" + datapointStr + ", mCurrentEventId=" + mCurrentEventRemoteId);
if (mDatapointsToUploadList != null) {
if (mDatapointsToUploadList.size() > 0) {
mDatapointsToUploadList.remove(0);
}
setDatapointToUploaded(mCurrentDatapointId, mCurrentEventId);
} else {
Log.w(TAG,"datapointCallback - mDatapointsToUploadList is null - I don't thin this should have happened!");
}
setDatapointToUploaded(mCurrentDatapointId, mCurrentEventRemoteId);
uploadNextDatapoint();
}
@@ -903,9 +1037,19 @@ public class LogManager {
String SQLStr = "CREATE TABLE IF NOT EXISTS " + mDpTableName + "("
+ "id INTEGER PRIMARY KEY,"
+ "dataTime DATETIME,"
+ "Status INT,"
+ "status INT,"
+ "dataJSON TEXT,"
+ "uploaded INT"
+ "uploaded TEXT" // Stores the ID of the datapoint in the remote database if uploaded, otherwise empty
+ ");";
db.execSQL(SQLStr);
Log.i(TAG, "onCreate - TableName=" + mEventsTableName);
SQLStr = "CREATE TABLE IF NOT EXISTS " + mEventsTableName + "("
+ "id INTEGER PRIMARY KEY,"
+ "dataTime DATETIME,"
+ "status INT,"
+ "type TEXT,"
+ "subType TEXT,"
+ "uploaded TEXT" // stores the id of the event in the remote dabase if uploaded, otherwise empty
+ ");";
db.execSQL(SQLStr);
}

View File

@@ -11,9 +11,11 @@ import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.IBinder;
import androidx.core.view.MenuCompat;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -219,14 +221,34 @@ public class LogManagerControlActivity extends AppCompatActivity {
// A bit of a hack to display in reverse chronological order
for (int i = eventsArray.length() - 1; i >= 0; i--) {
JSONObject eventObj = eventsArray.getJSONObject(i);
Long id = eventObj.getLong("id");
int osdAlarmState = eventObj.getInt("osdAlarmState");
String dataTime = eventObj.getString("dataTime");
String typeStr = eventObj.getString("type");
String subType = eventObj.getString("subType");
String desc = eventObj.getString("desc");
Log.v(TAG, "getRemoteEvents() - " + eventObj.toString());
String id = null;
if (!eventObj.isNull("id")) {
id = eventObj.getString("id");
}
int osdAlarmState = -1;
if (!eventObj.isNull("osdAlarmState")) {
osdAlarmState = eventObj.getInt("osdAlarmState");
}
String dataTime = "null";
if (!eventObj.isNull("dataTime")) {
dataTime = eventObj.getString("dataTime");
Log.v(TAG, "getRemoteEvents() - dataTime=" + dataTime);
}
String typeStr = "null";
if (!eventObj.isNull("type")) {
typeStr = eventObj.getString("type");
}
String subType = "null";
if (!eventObj.isNull("subType")) {
subType = eventObj.getString("subType");
}
String desc = "null";
if (!eventObj.isNull("desc")) {
desc = eventObj.getString("desc");
}
HashMap<String, String> eventHashMap = new HashMap<String, String>();
eventHashMap.put("id", String.valueOf(id));
eventHashMap.put("id", id);
eventHashMap.put("osdAlarmState", String.valueOf(osdAlarmState));
eventHashMap.put("osdAlarmStateStr", mUtil.alarmStatusToString(osdAlarmState));
eventHashMap.put("dataTime", dataTime);
@@ -249,7 +271,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
private void updateUi() {
Log.i(TAG,"updateUi()");
Log.i(TAG, "updateUi()");
boolean stopUpdating = true;
TextView tv;
Button btn;
@@ -291,7 +313,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
if (mRemoteEventsList != null) {
ListView lv = (ListView) findViewById(R.id.remoteEventsLv);
ListAdapter adapter = new RemoteEventsAdapter(LogManagerControlActivity.this, mRemoteEventsList, R.layout.log_entry_layout_remote,
new String[]{"id","dataTime", "type", "subType", "osdAlarmStateStr", "desc"},
new String[]{"id", "dataTime", "type", "subType", "osdAlarmStateStr", "desc"},
new int[]{R.id.event_id_remote_tv, R.id.event_date_remote_tv, R.id.event_type_remote_tv, R.id.event_subtype_remote_tv,
R.id.event_alarmState_remote_tv, R.id.event_notes_remote_tv});
lv.setAdapter(adapter);
@@ -335,7 +357,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
boolean checked = ((RadioButton) view).isChecked();
// Check which radio button was clicked
switch(view.getId()) {
switch (view.getId()) {
case R.id.local_data_rb:
if (checked) {
// Switch to the local data view
@@ -411,7 +433,6 @@ public class LogManagerControlActivity extends AppCompatActivity {
}
View.OnClickListener onAuth =
new View.OnClickListener() {
@Override
@@ -504,7 +525,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
Log.v(TAG, "onRemoteEventList Click() - Position=" + position + ", id=" + id);// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
HashMap<String, String> eventObj = (HashMap<String, String>) adapter.getItemAtPosition(position);
Long eventId = Long.parseLong(eventObj.get("id"));
String eventId = eventObj.get("id");
Log.d(TAG, "onItemClickListener(): eventId=" + eventId + ", eventObj=" + eventObj);
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
i.putExtra("eventId", eventId);
@@ -588,9 +609,9 @@ public class LogManagerControlActivity extends AppCompatActivity {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
Map<String, ?> dataItem = (Map<String,?>)getItem(position);
Log.v(TAG,"getView() "+dataItem.toString());
switch(dataItem.get("type").toString()) {
Map<String, ?> dataItem = (Map<String, ?>) getItem(position);
Log.v(TAG, "getView() " + dataItem.toString());
switch (dataItem.get("type").toString()) {
case "null":
v.setBackgroundColor(Color.parseColor("#ffaaaa"));
break;
@@ -603,20 +624,17 @@ public class LogManagerControlActivity extends AppCompatActivity {
// Convert date format to something more readable.
TextView tv = (TextView) v.findViewById(R.id.event_date_remote_tv);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date dataTime = dateFormat.parse(dataItem.get("dataTime").toString());
dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
Date dataTime = null;
String dateStr = (String) dataItem.get("dataTime");
dataTime = mUtil.string2date(dateStr);
if (dataTime != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
tv.setText(dateFormat.format(dataTime));
} catch (ParseException e) {
Log.e(TAG,"remoteEventsAdapter.getView: Error Parsing dataDate "+e.getLocalizedMessage());
} else {
tv.setText("---");
}
return(v);
return (v);
}
}
};
}

View File

@@ -631,7 +631,7 @@ public class MainActivity extends AppCompatActivity {
tv.setBackgroundColor(okColour);
tv.setTextColor(okTextColour);
if (!mConnection.mSdServer.mLm.mWac.mServerConnectionOk) {
if (!mConnection.mSdServer.mLm.mWac.checkServerConnection()) {
// Problem connecting to server
tv = (TextView) findViewById(R.id.remoteDbTv);
tv.setText(getString(R.string.data_sharing_status)

View File

@@ -59,6 +59,7 @@ import java.io.File;
import java.io.FileWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -453,6 +454,31 @@ public class OsdUtil {
}
}
/**
* string2date - returns a Date object represented by string dateStr
* It first attempts to parse it as a long integer, in which case it is assumed to
* be a unix timestamp.
* If that fails it attempts to parse it as yyyy-MM-dd'T'HH:mm:ss'Z' format.
* @param dateStr String reprenting a date
* @return Date object or null if parsing fails.
*/
public Date string2date(String dateStr) {
Date dataTime = null;
try {
Long tstamp = Long.parseLong(dateStr);
dataTime = new Date(tstamp);
} catch (NumberFormatException e) {
Log.v(TAG, "remoteEventsAdapter.getView: Error Parsing dataDate as Long: " + e.getLocalizedMessage()+" trying as string");
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dataTime = dateFormat.parse(dateStr);
} catch (ParseException e2) {
Log.e(TAG, "remoteEventsAdapter.getView: Error Parsing dataDate " + e2.getLocalizedMessage());
dataTime = null;
}
}
return(dataTime);
}
public final int ALARM_STATUS_WARNING = 1;

View File

@@ -17,6 +17,7 @@ import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.TimePicker;
import java.text.SimpleDateFormat;
import java.util.Calendar;
/**
@@ -40,6 +41,7 @@ public class ReportSeizureActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
mContext = this;
mUtil = new OsdUtil(this, serverStatusHandler);
if (!mUtil.isServerRunning()) {
mUtil.showToast(getString(R.string.error_server_not_running));
@@ -65,7 +67,7 @@ public class ReportSeizureActivity extends AppCompatActivity {
//mLm= new LogManager(mContext);
Button okBtn =
(Button) findViewById(R.id.OKBtn);
(Button) findViewById(R.id.loginBtn);
okBtn.setOnClickListener(onOk);
Button cancelBtn =
@@ -142,22 +144,12 @@ public class ReportSeizureActivity extends AppCompatActivity {
@Override
public void onClick(View view) {
Log.v(TAG, "onOk");
//SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr=String.format("%4d-%02d-%02d %02d:%02d:30",mYear,mMonth+1,mDay, mHour, mMinute);
Log.v(TAG, "onOk() - dateSTr="+dateStr);
mMsg = "Finding Nearest Datapoint to Date/Time "+dateStr+"...";
mLm.getNearestDatapointToDate(dateStr, (Long id) -> {
mMsg = mMsg + "\nNearest Datapoint is "+id;
Log.v(TAG, "onOK() - nearest datapoint is "+id);
if (id!=-1) {
mLm.setDatapointStatus(id,5);
mMsg = mMsg + "\nSet Datapoint to Manual Alarm Status";
mUtil.showToast(getString(R.string.createdNewEvent));
mLm.createLocalEvent(dateStr,5);
mUtil.showToast("Seizure Event Created");
finish();
} else {
mMsg = mMsg + "\n*** Datapoint not found - not doing anything ***";
mUtil.showToast(getString(R.string.DatapointNotFound));
}
});
}
};
View.OnClickListener onCancel =

View File

@@ -138,7 +138,7 @@ public class SdServer extends Service implements SdDataReceiver {
private long mEventsTimerPeriod = 60; // Number of seconds between checks to see if there are unvalidated remote events.
private long mEventDuration = 120; // event duration in seconds - uploads datapoints that cover this time range centred on the event time.
public long mDataRetentionPeriod = 1; // Prunes the local db so it only retains data younger than this duration (in days)
private long mRemoteLogPeriod = 60; // Period in seconds between uploads to the remote server.
private long mRemoteLogPeriod = 20; // Period in seconds between uploads to the remote server.
private long mAutoPrunePeriod = 3600; // Prune the database every hour
private boolean mAutoPruneDb;
@@ -1137,7 +1137,7 @@ public class SdServer extends Service implements SdDataReceiver {
//writeToSD();
mLm.writeDatapointToLocalDb(mSdData);
} else {
Log.e(TAG,"logData() - mLm is null - this should not happen");
Log.e(TAG, "logData() - mLm is null - this should not happen");
}
}
}
@@ -1241,7 +1241,7 @@ public class SdServer extends Service implements SdDataReceiver {
//prefVal = SP.getString("RemoteLogPeriod", "60");
//mRemoteLogPeriod = Integer.parseInt(prefVal);
mRemoteLogPeriod = 60;
//mRemoteLogPeriod = 60;
Log.v(TAG, "mRemoteLogPeriod=" + mRemoteLogPeriod);
//mOSDUname = SP.getString("OSDUname", "<username>");
@@ -1300,8 +1300,8 @@ public class SdServer extends Service implements SdDataReceiver {
Log.i(TAG, "SmsTimer.onFinish() - Last Location is Null so sending first SMS without location.");
}
} else {
Log.e(TAG,"SmsTImer.onFinish() - mLocationFinder is NULL - this should not happen!");
mUtil.showToast("Error Finding Location - mLocationFinder is null - please report this issue!");
Log.e(TAG,"SmsTimer.onFinish - mLocationFinder is null - this should not happen!");
mUtil.showToast("SmsTimer.onFinish - mLocationFinder is null - this should not happen! - Please report this issue!");
}
Log.i(TAG, "SmsTimer.onFinish() - Sending to " + mSMSNumbers.length + " Numbers");
mUtil.writeToSysLogFile("SdServer.SmsTimer.onFinish()");
@@ -1555,28 +1555,26 @@ public class SdServer extends Service implements SdDataReceiver {
// Retrieve events from remote database
if (mLm.mWac.getEvents((JSONObject remoteEventsObj) -> {
Log.v(TAG, "CheckEvents.getEvents.Callback()");
long firstUnvalidatedEvent;
Boolean haveUnvalidatedEvent = false;
if (remoteEventsObj == null) {
Log.e(TAG, "CheckEvents.Callback: Error Retrieving events");
} else {
try {
JSONArray eventsArray = remoteEventsObj.getJSONArray("events");
// A bit of a hack to display in reverse chronological order
firstUnvalidatedEvent = -1;
for (int i = eventsArray.length() - 1; i >= 0; i--) {
JSONObject eventObj = eventsArray.getJSONObject(i);
Long id = eventObj.getLong("id");
String typeStr = eventObj.getString("type");
//Log.v(TAG,"CheckEventsTimer: id="+id+", typeStr="+typeStr);
if (typeStr.equals("null")) {
firstUnvalidatedEvent = id;
if (typeStr.equals("null") || typeStr.equals("")) {
haveUnvalidatedEvent = true;
//Log.v(TAG,"CheckEventsTimer:setting firstUnvalidatedEvent to "+firstUnvalidatedEvent);
}
}
Log.v(TAG, "CheckEventsTimer.onFinish.callback - firstUnvalidatedEvent = " +
firstUnvalidatedEvent);
if (firstUnvalidatedEvent >= 0) {
showEventNotification(firstUnvalidatedEvent);
Log.v(TAG, "CheckEventsTimer.onFinish.callback - haveUnvalidatedEvent = " +
haveUnvalidatedEvent);
if (haveUnvalidatedEvent) {
showEventNotification();
mNM.cancel(DATASHARE_NOTIFICATION_ID);
} else {
mNM.cancel(EVENT_NOTIFICATION_ID);
@@ -1627,7 +1625,7 @@ public class SdServer extends Service implements SdDataReceiver {
/**
* Show a notification to tell the user that we have unvalidated events.
*/
private void showEventNotification(long eventId) {
private void showEventNotification() {
Log.v(TAG, "showEventNotification()");
int iconId;
String titleStr;

View File

@@ -4,6 +4,8 @@ import android.content.Context;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
@@ -12,6 +14,18 @@ import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.firestore.core.OrderBy;
import org.json.JSONArray;
import org.json.JSONException;
@@ -22,20 +36,19 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
// This class is intended to handle all interactions with the OSD WebAPI
public class WebApiConnection {
public String retVal;
public int retCode;
public boolean mServerConnectionOk = false;
private String mUrlBase = "https://osdApi.ddns.net";
public abstract class WebApiConnection {
protected Context mContext;
protected OsdUtil mUtil;
private String TAG = "WebApiConnection";
private String mAuthToken;
private Context mContext;
private OsdUtil mUtil;
RequestQueue mQueue;
public interface JSONObjectCallback {
public void accept(JSONObject retValObj);
@@ -51,237 +64,23 @@ public class WebApiConnection {
public WebApiConnection(Context context) {
mContext = context;
mQueue = Volley.newRequestQueue(context);
mUtil = new OsdUtil(mContext, new Handler());
}
public void close() {
Log.i(TAG,"stop()");
mQueue.stop();
Log.i(TAG, "stop()");
}
/**
* 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, StringCallback 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/
// sending the credentials as a JSONObject postData did not work, so try the method from:
// https://protocoderspoint.com/login-and-registration-form-in-android-using-volley-keeping-user-logged-in/#Login_Registration_form_in_android_using_volley_library
String urlStr = mUrlBase + "/api/accounts/login/";
Log.v(TAG, "urlStr=" + urlStr);
StringRequest req = new StringRequest(Request.Method.POST, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
String tokenStr = null;
Log.v(TAG, "Response is: " + response);
try {
JSONObject jo = new JSONObject(response);
tokenStr = jo.getString("token");
mServerConnectionOk = true;
} catch (JSONException e) {
tokenStr = "Error Parsing Rsponse";
}
setStoredToken(tokenStr);
callback.accept(tokenStr);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
Log.e(TAG, "Login Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Login Error: Returned null response");
}
mServerConnectionOk = false;
setStoredToken(null);
callback.accept(null);
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
params.put("login", uname);
params.put("password", passwd);
return params;
}
};
mQueue.add(req);
return (true);
}
// Remove the stored token so future calls are not authenticated.
public void logout() {
Log.v(TAG, "logout()");
setStoredToken(null);
//saveStoredToken(null);
}
public void setStoredToken(String authToken) {
mAuthToken = authToken;
}
private String getStoredToken() {
return (mAuthToken);
}
public boolean isLoggedIn() {
String authToken = getStoredToken();
//Log.v(TAG, "isLoggedIn(): token=" + authToken);
if (authToken == null || authToken.length() == 0) {
//Log.v(TAG, "isLogged in - not logged in");
return (false);
} else {
return (true);
}
}
public abstract boolean isLoggedIn();
// 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, StringCallback callback) {
Log.v(TAG, "createEvent()");
String urlStr = mUrlBase + "/api/events/";
Log.v(TAG, "urlStr=" + urlStr);
final String authtoken = getStoredToken();
// passes the newly created documentId to function callback on successful completion, or null on error.
public abstract boolean createEvent(final int osdAlarmState, final Date eventDate, final String eventDesc, StringCallback callback);
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("osdAlarmState", String.valueOf(osdAlarmState));
jsonObject.put("dataTime", dateFormat.format(eventDate));
jsonObject.put("desc", eventDesc);
} catch (JSONException e) {
Log.e(TAG, "Error generating event JSON string");
}
final String dataStr = jsonObject.toString();
Log.v(TAG, "createEvent - data=" + dataStr);
// calls function callback with a JSONObject representation of the event with id 'eventId'
public abstract boolean getEvent(String eventId, JSONObjectCallback callback);
StringRequest req = new StringRequest(Request.Method.POST, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
mServerConnectionOk = true;
callback.accept(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
callback.accept(null);
} else {
Log.e(TAG, "Create Event Error - null respones");
callback.accept(null);
}
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
String authToken = getStoredToken();
params.put("Authorization: Token " + authToken, authToken);
Log.v(TAG, "getParams: params=" + params.toString());
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {
return dataStr == null ? null : dataStr.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", dataStr, "utf-8");
return null;
}
}
};
mQueue.add(req);
return (true);
}
public boolean getEvent(Long eventId, JSONObjectCallback callback) {
//Long eventId=Long.valueOf(285);
Log.v(TAG, "getEvent()");
String urlStr = mUrlBase + "/api/events/" + eventId;
Log.v(TAG, "getEvent(): urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
try {
JSONObject retObj = new JSONObject(response);
retObj.put("alarmStateStr", mUtil.alarmStatusToString(retObj.getInt("osdAlarmState")));
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
mServerConnectionOk = true;
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Create Event Error: returned null response");
}
mServerConnectionOk = false;
callback.accept(null);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
/**
* Retrieve all events accessible to the logged in user, and pass them to the callback function as a JSONObject
@@ -289,305 +88,11 @@ public class WebApiConnection {
* @param callback
* @return true on success or false on failure to initiate the request.
*/
public boolean getEvents(JSONObjectCallback callback) {
//Long eventId=Long.valueOf(285);
Log.v(TAG, "getEvents()");
String urlStr = mUrlBase + "/api/events/";
Log.v(TAG, "getEvents(): urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
mServerConnectionOk = true;
try {
JSONObject retObj = new JSONObject();
JSONArray eventArray = new JSONArray(response);
retObj.put("events", eventArray);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//if ((error != null) && (error.networkResponse != null) && (error.networkResponse.data != null)) {#
mServerConnectionOk = false;
if (error != null) {
if (error.networkResponse != null) {
Log.e(TAG, "getEvents(): Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "getEvents(): Error: - request returned null networkResponse");
}
} else{
Log.e(TAG, "getEvents(): Error: - request returned null response");
}
callback.accept(null);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
public boolean updateEvent(final JSONObject eventObj, JSONObjectCallback callback) {
Long eventId;
Log.v(TAG, "updateEvent()");
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
try {
eventId = eventObj.getLong("id");
} catch (JSONException e) {
Log.e(TAG, "updateEvent(): Error reading id from eventObj");
eventId = Long.valueOf(-1);
}
final String dataStr = eventObj.toString();
Log.v(TAG, "createEvent - data=" + dataStr);
int reqMethod;
String urlStr;
if (eventId != -1) {
Log.v(TAG, "updateEvent() - found eventId " + eventId + ", Updating event record");
urlStr = mUrlBase + "/api/events/" + eventId + "/";
Log.v(TAG, "urlStr=" + urlStr);
reqMethod = Request.Method.PUT;
} else {
Log.v(TAG, "updateEvent() - eventId not found - creating new event record");
urlStr = mUrlBase + "/api/events/";
Log.v(TAG, "urlStr=" + urlStr);
reqMethod = Request.Method.POST;
}
StringRequest req = new StringRequest(reqMethod, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
mServerConnectionOk = true;
try {
JSONObject retObj = new JSONObject(response);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Create Event Error - returned null response");
}
callback.accept(null);
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
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;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {
return dataStr == null ? null : dataStr.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", dataStr, "utf-8");
return null;
}
}
};
mQueue.add(req);
return (true);
}
public boolean createDatapoint(JSONObject dataObj, int eventId, StringCallback callback) {
Log.v(TAG, "createDatapoint()");
// Create a new event in the remote database, based on the provided parameters.
String urlStr = mUrlBase + "/api/datapoints/";
Log.v(TAG, "urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
JSONObject jsonObject = new JSONObject();
try {
//jsonObject.put("userId", -1);
jsonObject.put("eventId", String.valueOf(eventId));
jsonObject.put("dataTime", dataObj.getString("dataTime"));
jsonObject.put("dataJSON", dataObj.toString());
} catch (JSONException e) {
Log.e(TAG, "Error generating event JSON string");
}
final String dataStr = jsonObject.toString();
Log.v(TAG, "createDatapoint - dataStr=" + dataStr);
StringRequest req = new StringRequest(Request.Method.POST, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
mServerConnectionOk = true;
callback.accept(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "Create Datapoint Error: " + error.toString() + ", message:" + error.getMessage());
callback.accept(null);
} else {
Log.e(TAG, "Create Datapoint Error - returned null respones");
callback.accept(null);
}
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
String authToken = getStoredToken();
params.put("Authorization: Token " + authToken, authToken);
Log.v(TAG, "getParams: params=" + params.toString());
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {
return dataStr == null ? null : dataStr.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", dataStr, "utf-8");
return null;
}
}
};
mQueue.add(req);
return (true);
}
/**
* Retieve the user profile of the authenticated user from the server, and return it to the callback function.
* @param callback - function to be called with a JSONObject as a parameter that contains the user profile data.
* @return true if request sent successfully, or else false.
*/
public boolean getUserProfile(JSONObjectCallback callback) {
Log.v(TAG, "getUserProfile()");
String urlStr = mUrlBase + "/api/accounts/profile/";
Log.v(TAG, "getUserProfile(): urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
try {
JSONObject retObj = new JSONObject(response);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getUserProfile.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
mServerConnectionOk = true;
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Create Event Error: returned null response");
}
mServerConnectionOk = false;
callback.accept(null);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
public abstract boolean getEvents(JSONObjectCallback callback);
public abstract boolean updateEvent(final JSONObject eventObj, JSONObjectCallback callback);
public abstract boolean createDatapoint(JSONObject dataObj, String eventId, StringCallback callback);
/**
* Retrieve the file containing the standard event types from the server.
@@ -595,88 +100,37 @@ public class WebApiConnection {
*
* @return true if request sent successfully or else false.
*/
public boolean getEventTypes(JSONObjectCallback callback) {
Log.v(TAG, "getEventTypes()");
String urlStr = mUrlBase + "/static/eventTypes.json";
Log.v(TAG, "urlStr=" + urlStr);
final String authtoken = getStoredToken();
public abstract boolean getEventTypes(JSONObjectCallback callback);
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "getEventTypes.onResponse(): Response is: " + response);
mServerConnectionOk = true;
try {
JSONObject retObj = new JSONObject(response);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "getEventTypes.onErrorResponse(): " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "getEventTypes.onErrorResponse() - returned null response");
}
callback.accept(null);
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
/**
* Retrieve a trivial file from the server to check we have a good server connection.
* sets mServerConnectionOk.
*
* @return true if request sent successfully or else false.
*/
public boolean checkServerConnection() {
Log.v(TAG, "checkServerConnection()");
String urlStr = mUrlBase + "/static/test.txt";
Log.v(TAG, "urlStr=" + urlStr);
public abstract boolean checkServerConnection();
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "checkServerConnection.onResponse(): Response is: " + response);
mServerConnectionOk = true;
public abstract boolean getUserProfile(JSONObjectCallback callback);
public boolean authenticate(final String uname, final String passwd, StringCallback callback) {
Log.e(TAG,"WebApiConnection.authenticate(username, password, callback) Not Implemented");
return false;
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.v(TAG, "checkServerConnection.onErrorResponse");
mServerConnectionOk = false;
// Remove the stored token so future calls are not authenticated.
public void logout() {
Log.v(TAG, "logout()");
setStoredToken(null);
}
});
mQueue.add(req);
return (true);
protected void setStoredToken(String authToken) {
mAuthToken = authToken;
}
protected String getStoredToken() {
return (mAuthToken);
}
}

View File

@@ -0,0 +1,423 @@
package uk.org.openseizuredetector;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// This class is intended to handle all interactions with the OSD WebAPI
public class WebApiConnection_firebase extends WebApiConnection {
public String retVal;
public int retCode;
public boolean mServerConnectionOk = false;
private String TAG = "WebApiConnection_firebase";
private String mAuthToken;
private Context mContext;
private OsdUtil mUtil;
FirebaseFirestore mDb;
RequestQueue mQueue;
public WebApiConnection_firebase(Context context) {
super(context);
loginToFirebase();
}
public void loginToFirebase() {
// Check if we are already logged in
FirebaseAuth auth = FirebaseAuth.getInstance();
mDb = FirebaseFirestore.getInstance();
if (auth != null) {
if (auth.getCurrentUser() != null) {
Log.i(TAG, "Firebase Logged in OK -" + auth.getCurrentUser().getDisplayName());
} else {
Log.e(TAG, "Firebase not logged in - no current user");
}
} else {
Log.e(TAG, "Firebase not logged in");
}
}
public void close() {
Log.i(TAG, "stop()");
mQueue.stop();
}
public boolean isLoggedIn() {
FirebaseAuth auth = FirebaseAuth.getInstance();
if (auth != null) {
if (auth.getCurrentUser() != null) {
//Log.v(TAG, "isLoggedIn(): Firebase Logged in OK");
return (true);
} else {
//Log.v(TAG, "isLoggedIn(): Current user is null - Firebase not logged in");
return (false);
}
} else {
//Log.v(TAG, "isLoggedIn(): Firebase not logged in");
return (false);
}
}
public boolean getUserProfile(JSONObjectCallback callback) {
Log.v(TAG, "getUserProfile()");
FirebaseAuth auth = FirebaseAuth.getInstance();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
} else {
try {
JSONObject retObj = new JSONObject();
retObj.put("id",auth.getCurrentUser().getUid());
retObj.put("username", auth.getCurrentUser().getDisplayName());
retObj.put("email", auth.getCurrentUser().getEmail());
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "Error Creating retObjObj: " + e.getMessage());
mUtil.showToast("Error Creating retObj - this should not happen!!!");
return (false);
}
}
return (true);
}
public String getStoredToken() {
return null;
}
public void setStoredToken(String s) {
return;
}
// Create a new event in the remote database, based on the provided parameters.
// passes the newly created documentId to function callback on successful completion, or null on error.
public boolean createEvent(final int osdAlarmState, final Date eventDate, final String eventDesc, StringCallback callback) {
Log.v(TAG, "createEvent()");
String userId = null;
if (mDb == null) {
Log.w(TAG, "createEvent() - mDb is null - not doing anything");
return false;
}
if (FirebaseAuth.getInstance().getCurrentUser() == null) {
Log.e(TAG, "ERROR: createEvent() - not logged in");
return false;
} else {
userId = FirebaseAuth.getInstance().getCurrentUser().getUid();
}
Map<String, Object> event = new HashMap<>();
event.put("dataTime", eventDate.getTime());
event.put("osdAlarmState", osdAlarmState);
event.put("desc", eventDesc);
event.put("type", null);
event.put("subType", null);
event.put("userId", userId);
mDb.collection("Events")
.add(event)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
@Override
public void onSuccess(DocumentReference documentReference) {
Log.d(TAG, "createEvent.onSuccess() - DocumentSnapshot added with ID: " + documentReference.getId());
mServerConnectionOk = true;
callback.accept(documentReference.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.w(TAG, "createEvent.onFailure() - Error adding document", e);
callback.accept(null);
}
});
return (true);
}
// calls function callback with a JSONObject representation of the event with id 'eventId'
public boolean getEvent(String eventId, JSONObjectCallback callback) {
Log.v(TAG, "getEvent()");
if (mDb == null) {
Log.w(TAG, "getEvent() - mDb is null - not doing anything");
return false;
}
DocumentReference docRef = mDb
.collection("Events").document(eventId);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d(TAG, "getEvent.onComplete(): DocumentSnapshot data: " + document.getData());
if (document.getData() == null) {
callback.accept(null);
} else
callback.accept(new JSONObject(document.getData()));
} else {
Log.d(TAG, "No such document");
callback.accept(null);
}
} else {
Log.d(TAG, "get failed with ", task.getException());
callback.accept(null);
}
}
});
return true;
}
/**
* Retrieve all events accessible to the logged in user, and pass them to the callback function as a JSONObject
*
* @param callback
* @return true on success or false on failure to initiate the request.
*/
public boolean getEvents(JSONObjectCallback callback) {
//Long eventId=Long.valueOf(285);
Log.v(TAG, "getEvents()");
if (mDb == null) {
Log.w(TAG, "getEvents() - mDb is null - not doing anything");
return false;
}
if (!isLoggedIn()) {
Log.w(TAG, "getEvents() - not logged in - not doing anything");
return false;
}
String userId = FirebaseAuth.getInstance().getCurrentUser().getUid();
mDb.collection("Events") //.where("userId", "==", userId)
.whereEqualTo("userId", userId)
.orderBy("dataTime", Query.Direction.ASCENDING)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
try {
JSONObject retObj = new JSONObject();
JSONArray eventArray = new JSONArray();
Log.d(TAG, "getEvents() - returned " + task.getResult().size());
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, "getEvents() - " + document.getId() + " => " + document.getData());
JSONObject eventObj = new JSONObject(document.getData());
// Add the event id into the event data because firebase does not include it as part of the document.
eventObj.put("id", document.getId());
eventArray.put(eventObj);
}
retObj.put("events", eventArray);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEvents.onResponse(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
callback.accept(null);
}
}
});
return (true);
}
public boolean updateEvent(final JSONObject eventObj, JSONObjectCallback callback) {
String eventId;
Log.v(TAG, "updateEvent()");
if (mDb == null) {
Log.w(TAG, "updateEvent() - mDb is null - not doing anything");
return false;
}
try {
eventId = eventObj.getString("id");
} catch (JSONException e) {
Log.e(TAG, "updateEvent(): Error reading id from eventObj");
eventId = null;
return false;
}
final String dataStr = eventObj.toString();
Log.v(TAG, "updateEvent - data=" + dataStr);
Map<String, Object> eventMap = new HashMap<>();
try {
eventMap.put("dataTime", eventObj.getLong("dataTime"));
eventMap.put("osdAlarmState", eventObj.getInt("osdAlarmState"));
eventMap.put("desc", eventObj.getString("desc"));
eventMap.put("type", eventObj.getString("type"));
eventMap.put("subType", eventObj.getString("subType"));
eventMap.put("userId", eventObj.getString("userId"));
} catch (JSONException e) {
Log.e(TAG, "updateEvent(): Error data from eventObj." + e.toString());
e.printStackTrace();
return false;
}
Log.v(TAG, "updateEvent - map=" + eventMap.toString());
try {
DocumentReference docRef = mDb.collection("Events").document(eventId);
docRef.set(eventMap)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
JSONObject retObj;
try {
retObj = new JSONObject("{\"status\":\"OK\"}");
} catch (Exception e) {
retObj = null;
}
callback.accept(retObj);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.w(TAG, "Error updating document", e);
callback.accept(null);
}
});
return (true);
} catch (Exception e) {
Log.e(TAG, "updateEvent() - ERROR: " + e.toString());
e.printStackTrace();
}
return (false);
}
public boolean createDatapoint(JSONObject dataObj, String eventId, StringCallback callback) {
Log.v(TAG, "createDatapoint()");
// Create a new event in the remote database, based on the provided parameters.
String userId = null;
if (FirebaseAuth.getInstance().getCurrentUser() == null) {
Log.e(TAG, "ERROR: createDatapoint() - not logged in");
return false;
} else {
userId = FirebaseAuth.getInstance().getCurrentUser().getUid();
}
String dataTime;
try {
dataTime = dataObj.getString("dataTime");
} catch (JSONException e) {
dataTime = "";
}
Map<String, Object> datapoint = new HashMap<>();
datapoint.put("dataTime", dataTime);
datapoint.put("dataJSON", dataObj.toString());
datapoint.put("userId", userId);
datapoint.put("eventId", userId);
mDb.collection("Datapoints")
.add(datapoint)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
@Override
public void onSuccess(DocumentReference documentReference) {
Log.d(TAG, "createDatapoint.onSuccess() - DocumentSnapshot added with ID: " + documentReference.getId());
mServerConnectionOk = true;
callback.accept(documentReference.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.w(TAG, "createDatapoint.onFailure() - Error adding document", e);
callback.accept(null);
}
});
return (true);
}
/**
* Retrieve the file containing the standard event types from the server.
* Calls the specified callback function, passing a JSONObject as a parameter when the data has been received and parsed.
*
* @return true if request sent successfully or else false.
*/
public boolean getEventTypes(JSONObjectCallback callback) {
Log.v(TAG, "getEventTypes()");
if (mDb == null) {
Log.w(TAG, "getEventTypes() - mDb is null - not doing anything");
return false;
}
mDb.collection("EventTypes")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
try {
JSONObject retObj = new JSONObject();
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, "getEventTypes.onComplete(): " + document.getId() + " => " + document.getData());
Log.v(TAG, "getEventTypes.onComplete() - subtypes=" + document.getData().get("subTypes"));
JSONArray subTypesArray = listToJSONArray((List) document.getData().get("subTypes"));
retObj.put(document.getData().get("type").toString(), subTypesArray);
}
Log.d(TAG, "getEventTypes.onComplete() - retObj=" + retObj.toString());
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onResponse(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
callback.accept(null);
}
}
});
return (true);
}
private JSONArray listToJSONArray(List<Object> list) {
JSONArray arr = new JSONArray();
for (Object obj : list) {
arr.put(obj);
}
return arr;
}
/**
* Retrieve a trivial file from the server to check we have a good server connection.
* sets mServerConnectionOk.
*
* @return true if request sent successfully or else false.
*/
public boolean checkServerConnection() {
//FIXME There must be a Firebase function for this?
mServerConnectionOk = true;
return mServerConnectionOk;
}
}

View File

@@ -0,0 +1,663 @@
package uk.org.openseizuredetector;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
// This class is intended to handle all interactions with the OSD WebAPI
public class WebApiConnection_osdapi extends WebApiConnection {
public String retVal;
public int retCode;
public boolean mServerConnectionOk = false;
private String mUrlBase = "https://osdApi.ddns.net";
private String TAG = "WebApiConnection_osdapi";
RequestQueue mQueue;
public WebApiConnection_osdapi(Context context) {
super(context);
mQueue = Volley.newRequestQueue(context);
}
public void close() {
super.close();
Log.i(TAG,"stop()");
mQueue.stop();
}
/**
* 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.
*/
@Override
public boolean authenticate(final String uname, final String passwd, StringCallback 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/
// sending the credentials as a JSONObject postData did not work, so try the method from:
// https://protocoderspoint.com/login-and-registration-form-in-android-using-volley-keeping-user-logged-in/#Login_Registration_form_in_android_using_volley_library
String urlStr = mUrlBase + "/api/accounts/login/";
Log.v(TAG, "urlStr=" + urlStr);
StringRequest req = new StringRequest(Request.Method.POST, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
String tokenStr = null;
Log.v(TAG, "Response is: " + response);
try {
JSONObject jo = new JSONObject(response);
tokenStr = jo.getString("token");
mServerConnectionOk = true;
} catch (JSONException e) {
tokenStr = "Error Parsing Rsponse";
}
setStoredToken(tokenStr);
callback.accept(tokenStr);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
Log.e(TAG, "Login Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Login Error: Returned null response");
}
mServerConnectionOk = false;
setStoredToken(null);
callback.accept(null);
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
params.put("login", uname);
params.put("password", passwd);
return params;
}
};
mQueue.add(req);
return (true);
}
public boolean isLoggedIn() {
String authToken = getStoredToken();
Log.v(TAG, "isLoggedIn(): token=" + authToken);
if (authToken == null || authToken.length() == 0) {
Log.v(TAG, "isLogged in - not logged in");
return (false);
} else {
Log.v(TAG,"isLoggedIn - logged in ok");
return (true);
}
}
// 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, StringCallback callback) {
Log.v(TAG, "createEvent()");
String urlStr = mUrlBase + "/api/events/";
Log.v(TAG, "urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("osdAlarmState", String.valueOf(osdAlarmState));
jsonObject.put("dataTime", dateFormat.format(eventDate));
jsonObject.put("desc", eventDesc);
} catch (JSONException e) {
Log.e(TAG, "Error generating event JSON string");
}
final String dataStr = jsonObject.toString();
Log.v(TAG, "createEvent - data=" + dataStr);
StringRequest req = new StringRequest(Request.Method.POST, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "createEvent.onResponse - Response is: " + response);
mServerConnectionOk = true;
// we return just the eventId to be consistent with the firebase version of WebApiConnection.
String retVal = null;
try {
JSONObject retObj = new JSONObject(response);
retVal = retObj.getString("id");
} catch (JSONException e) {
Log.e(TAG, "createEvent.onResponse(): Error: " + e.getMessage() + "," + e.toString());
retVal = null;
}
callback.accept(retVal);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "createEvent Error: " + error.toString() + ", message:" + error.getMessage());
callback.accept(null);
} else {
Log.e(TAG, "createEvent Error - null response");
callback.accept(null);
}
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
String authToken = getStoredToken();
params.put("Authorization: Token " + authToken, authToken);
Log.v(TAG, "getParams: params=" + params.toString());
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {
return dataStr == null ? null : dataStr.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", dataStr, "utf-8");
return null;
}
}
};
mQueue.add(req);
return (true);
}
public boolean getEvent(String eventId, JSONObjectCallback callback) {
Log.v(TAG, "getEvent()");
String urlStr = mUrlBase + "/api/events/" + eventId;
Log.v(TAG, "getEvent(): urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
try {
JSONObject retObj = new JSONObject(response);
retObj.put("alarmStateStr", mUtil.alarmStatusToString(retObj.getInt("osdAlarmState")));
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
mServerConnectionOk = true;
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Create Event Error: returned null response");
}
mServerConnectionOk = false;
callback.accept(null);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
/**
* Retrieve all events accessible to the logged in user, and pass them to the callback function as a JSONObject
*
* @param callback
* @return true on success or false on failure to initiate the request.
*/
public boolean getEvents(JSONObjectCallback callback) {
Log.v(TAG, "getEvents()");
String urlStr = mUrlBase + "/api/events/";
Log.v(TAG, "getEvents(): urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
mServerConnectionOk = true;
try {
JSONObject retObj = new JSONObject();
JSONArray eventArray = new JSONArray(response);
retObj.put("events", eventArray);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//if ((error != null) && (error.networkResponse != null) && (error.networkResponse.data != null)) {#
mServerConnectionOk = false;
if (error != null) {
if (error.networkResponse != null) {
Log.e(TAG, "getEvents(): Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "getEvents(): Error: - request returned null networkResponse");
}
} else{
Log.e(TAG, "getEvents(): Error: - request returned null response");
}
callback.accept(null);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
public boolean updateEvent(final JSONObject eventObj, JSONObjectCallback callback) {
String eventId;
Log.v(TAG, "updateEvent()");
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
try {
eventId = eventObj.getString("id");
} catch (JSONException e) {
Log.e(TAG, "updateEvent(): Error reading id from eventObj");
eventId = null;
}
final String dataStr = eventObj.toString();
Log.v(TAG, "updateEvent - data=" + dataStr);
int reqMethod;
String urlStr;
if (eventId != null) {
Log.v(TAG, "updateEvent() - found eventId " + eventId + ", Updating event record");
urlStr = mUrlBase + "/api/events/" + eventId + "/";
Log.v(TAG, "urlStr=" + urlStr);
reqMethod = Request.Method.PUT;
} else {
Log.v(TAG, "updateEvent() - eventId not found - creating new event record");
urlStr = mUrlBase + "/api/events/";
Log.v(TAG, "urlStr=" + urlStr);
reqMethod = Request.Method.POST;
}
StringRequest req = new StringRequest(reqMethod, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
mServerConnectionOk = true;
try {
JSONObject retObj = new JSONObject(response);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Create Event Error - returned null response");
}
callback.accept(null);
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
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;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {
return dataStr == null ? null : dataStr.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", dataStr, "utf-8");
return null;
}
}
};
mQueue.add(req);
return (true);
}
public boolean createDatapoint(JSONObject dataObj, String eventId, StringCallback callback) {
Log.v(TAG, "createDatapoint()");
// Create a new event in the remote database, based on the provided parameters.
String urlStr = mUrlBase + "/api/datapoints/";
Log.v(TAG, "urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
JSONObject jsonObject = new JSONObject();
try {
//jsonObject.put("userId", -1);
jsonObject.put("eventId", String.valueOf(eventId));
jsonObject.put("dataTime", dataObj.getString("dataTime"));
jsonObject.put("dataJSON", dataObj.toString());
} catch (JSONException e) {
Log.e(TAG, "Error generating event JSON string");
}
final String dataStr = jsonObject.toString();
Log.v(TAG, "createDatapoint - dataStr=" + dataStr);
StringRequest req = new StringRequest(Request.Method.POST, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
mServerConnectionOk = true;
callback.accept(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "Create Datapoint Error: " + error.toString() + ", message:" + error.getMessage());
callback.accept(null);
} else {
Log.e(TAG, "Create Datapoint Error - returned null respones");
callback.accept(null);
}
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// params.put("name",sname); // passing parameters to server
String authToken = getStoredToken();
params.put("Authorization: Token " + authToken, authToken);
Log.v(TAG, "getParams: params=" + params.toString());
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {
return dataStr == null ? null : dataStr.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", dataStr, "utf-8");
return null;
}
}
};
mQueue.add(req);
return (true);
}
/**
* Retieve the user profile of the authenticated user from the server, and return it to the callback function.
* @param callback - function to be called with a JSONObject as a parameter that contains the user profile data.
* @return true if request sent successfully, or else false.
*/
public boolean getUserProfile(JSONObjectCallback callback) {
Log.v(TAG, "getUserProfile()");
String urlStr = mUrlBase + "/api/accounts/profile/";
Log.v(TAG, "getUserProfile(): urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "Response is: " + response);
try {
JSONObject retObj = new JSONObject(response);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getUserProfile.onResponse(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
mServerConnectionOk = true;
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "Create Event Error: returned null response");
}
mServerConnectionOk = false;
callback.accept(null);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
/**
* Retrieve the file containing the standard event types from the server.
* Calls the specified callback function, passing a JSONObject as a parameter when the data has been received and parsed.
*
* @return true if request sent successfully or else false.
*/
public boolean getEventTypes(JSONObjectCallback callback) {
Log.v(TAG, "getEventTypes()");
String urlStr = mUrlBase + "/static/eventTypes.json";
Log.v(TAG, "urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) {
Log.v(TAG, "not logged in - doing nothing");
return (false);
}
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "getEventTypes.onResponse(): Response is: " + response);
mServerConnectionOk = true;
try {
JSONObject retObj = new JSONObject(response);
callback.accept(retObj);
} catch (JSONException e) {
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
callback.accept(null);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mServerConnectionOk = false;
if (error != null) {
Log.e(TAG, "getEventTypes.onErrorResponse(): " + error.toString() + ", message:" + error.getMessage());
} else {
Log.e(TAG, "getEventTypes.onErrorResponse() - returned null response");
}
callback.accept(null);
}
}) {
// Note, this is overriding part of StringRequest, not one of the sub-classes above!
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "Token " + getStoredToken());
return params;
}
};
mQueue.add(req);
return (true);
}
/**
* Retrieve a trivial file from the server to check we have a good server connection.
* sets mServerConnectionOk.
* @return true if request sent successfully or else false.
*/
public boolean checkServerConnection() {
Log.v(TAG, "checkServerConnection()");
String urlStr = mUrlBase + "/static/test.txt";
Log.v(TAG, "urlStr=" + urlStr);
StringRequest req = new StringRequest(Request.Method.GET, urlStr,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.v(TAG, "checkServerConnection.onResponse(): Response is: " + response);
mServerConnectionOk = true;
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.v(TAG, "checkServerConnection.onErrorResponse");
mServerConnectionOk = false;
}
});
mQueue.add(req);
return (true);
}
}

View File

@@ -31,6 +31,36 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/cancelBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/back" />
<Button
android:id="@+id/loginBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/authenticate" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/login_osdapi_ui"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
@@ -53,30 +83,12 @@
android:hint="password"
android:inputType="textPassword" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/cancelBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/back" />
<Button
android:id="@+id/OKBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/authenticate" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="horizontal"
>
<Button
android:id="@+id/RegisterBtn"
@@ -94,6 +106,8 @@
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/logout_ui"
android:layout_width="fill_parent"
@@ -117,6 +131,12 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="userId" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
@@ -139,12 +159,6 @@
</LinearLayout>
<!--<TextView
android:id="@+id/tokenTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/logged_in_with_token" />
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@@ -164,25 +178,6 @@
android:layout_weight="1"
android:text="@string/logout" />
</LinearLayout>
<!--
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/createEventBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Create Event" />
<Button
android:id="@+id/createDatapointBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Prune Database" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
@@ -190,14 +185,22 @@
android:orientation="horizontal">
<Button
android:id="@+id/getLocalEventsBtn"
android:id="@+id/aboutDataSharingBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="getLocalEvents" />
android:text="@string/about_data_sharing" />
<Button
android:id="@+id/privacyPolicyBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/privacy_policy" />
</LinearLayout>
-->
</LinearLayout>
</LinearLayout>

View File

@@ -17,7 +17,7 @@
android:text="@string/cancel" />
<Button
android:id="@+id/OKBtn"
android:id="@+id/loginBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"

View File

@@ -96,7 +96,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/OKBtn"
android:id="@+id/loginBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/okBtnTxt" />

View File

@@ -3,7 +3,7 @@
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView

View File

@@ -5,19 +5,12 @@
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/event_id_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:textAppearance="?android:attr/textAppearanceLarge" />
android:text="eventId" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -25,8 +18,8 @@
android:id="@+id/event_date_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="date" />
android:text="date"
android:textStyle="bold" />
<LinearLayout
android:layout_width="fill_parent"
@@ -37,8 +30,8 @@
android:id="@+id/event_type_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="---" />
android:text="---"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
@@ -52,24 +45,27 @@
android:text="---" />
</LinearLayout>
<TextView
android:id="@+id/event_alarmState_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alarm" />
<TextView
android:id="@+id/event_notes_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="notes"
android:textStyle="italic"
android:id="@+id/event_notes_remote_tv" />
android:textStyle="italic" />
<!-- <TextView
<TextView
android:id="@+id/event_id_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dataJSON"
android:id="@+id/event_dataJSON" />
-->
android:layout_marginRight="10dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
android:text="eventId" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -3,6 +3,8 @@
<string name="app_name">OpenSeizureDetector</string>
<string name="changelog">
"\n
\nV4.1.1 - Changed remote database to be compatible with either OSD webAPI or future Firebase database.<br/>
- Performance improvement by having separate events and datapoints tables in local database
\nV4.0.1 - fixed crash in Report Seizure function
\nV4.0.0
- Introduced the &lt;b>Data Sharing&lt;/b> feature to allow users to share their seizure and false alarm data
@@ -370,7 +372,7 @@
<string name="check_seizures_message">Please select the events highlighted in pink to say if they are real seizures or false alarms</string>
<string name="error_server_not_running">ERROR: OpenSeizureDetector Server is not running - please re-start it</string>
<string name="system_logs">System Logs</string>
<string name="logged_in_as_user_id">Logged in as User Id:</string>
<string name="logged_in_as_user_id">Logged in as: </string>
<string name="datasharing_notification_text">Select for more information</string>
<string name="datasharing_notification_title">OpenSeizureDetector Data Sharing Problem</string>
<string name="datasharing_about_title">OpenSeizureDetector Data Sharing</string>
@@ -430,4 +432,6 @@
and selecting <b>Apps->OpenSeizureDetector->Permissions</b>.
</string>
<string name="permissions_required">Permissions Disclosure</string>
<string name="about_data_sharing">About Data Sharing</string>
<string name="privacy_policy">Privacy Policy</string>
</resources>

View File

@@ -15,20 +15,14 @@ import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = {Build.VERSION_CODES.O_MR1}, packageName = "uk.org.openseizuredetector")
public class LogManagerTest extends TestCase {
LogManager mLm;
public void setUp() throws Exception {
super.setUp();
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
mLm = new LogManager(RuntimeEnvironment.systemContext);
}
public void tearDown() throws Exception {
mLm.close();
}
SdData getFakeSdData() {
SdData sdData = new SdData();
return sdData;
return null;
}

View File

@@ -0,0 +1,62 @@
package uk.org.openseizuredetector;
import static org.junit.Assert.*;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.firebase.FirebaseApp;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class WebApiConnectionTest {
WebApiConnection mWac;
@Before
public void setUp() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
FirebaseApp.initializeApp(context);
mWac = new WebApiConnection(context);
}
@After
public void tearDown() throws Exception {
}
@Test
public void isLoggedIn() {
assertTrue(mWac.isLoggedIn());
assertFalse(mWac.isLoggedIn());
}
@Test
public void createEvent() {
}
@Test
public void getEvent() {
}
@Test
public void getEvents() {
}
@Test
public void updateEvent() {
}
@Test
public void createDatapoint() {
}
@Test
public void getUserProfile() {
}
}

View File

@@ -10,6 +10,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.0'
classpath 'com.google.gms:google-services:4.3.10'
}
}
allprojects {
@@ -19,6 +20,7 @@ allprojects {
url 'https://maven.google.com/'
name 'Google'
}
google()
//maven { url 'https://jitpack.io' }
}
tasks.withType(JavaCompile) {