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/build
app/app.iml app/app.iml
app/release/output-metadata.json app/release/output-metadata.json
app/google-services.json
*# *#

View File

@@ -1,5 +1,5 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android { android {
compileSdkVersion 31 compileSdkVersion 31
useLibrary 'org.apache.http.legacy' useLibrary 'org.apache.http.legacy'
@@ -37,6 +37,8 @@ dependencies {
// Unit testing dependencies // Unit testing dependencies
implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.android.material:material:1.4.0' 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' testImplementation 'junit:junit:4.13.2'
// Set this dependency if you want to use Mockito // Set this dependency if you want to use Mockito
testImplementation 'org.mockito:mockito-core:4.3.1' testImplementation 'org.mockito:mockito-core:4.3.1'
@@ -53,7 +55,10 @@ dependencies {
//implementation 'com.github.RohitSurwase.UCE-Handler:uce_handler:1.3' //implementation 'com.github.RohitSurwase.UCE-Handler:uce_handler:1.3'
testImplementation 'org.robolectric:robolectric:4.7.3' testImplementation 'org.robolectric:robolectric:4.7.3'
implementation 'com.android.volley:volley:1.2.1' 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 { repositories {

View File

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

View File

@@ -5,6 +5,9 @@ import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
@@ -15,19 +18,28 @@ import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; 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.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.Arrays;
public class AuthenticateActivity extends AppCompatActivity { public class AuthenticateActivity extends AppCompatActivity {
private String TAG = "AuthenticateActivity"; private String TAG = "AuthenticateActivity";
private OsdUtil mUtil;
private EditText mUnameEt; private EditText mUnameEt;
private EditText mPasswdEt; private EditText mPasswdEt;
private SdServiceConnection mConnection;
final Handler serverStatusHandler = new Handler();
private WebApiConnection mWac; private WebApiConnection mWac;
private LogManager mLm; private LogManager mLm;
private SdServiceConnection mConnection; private static final String TOKEN_ID = "webApiAuthToken";
private OsdUtil mUtil;
final Handler serverStatusHandler = new Handler();
private String TOKEN_ID = "webApiAuthToken";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -36,50 +48,88 @@ public class AuthenticateActivity extends AppCompatActivity {
setContentView(R.layout.activity_authenticate); setContentView(R.layout.activity_authenticate);
mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler); mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler);
if (!mUtil.isServerRunning()) { if (!mUtil.isServerRunning()) {
mUtil.showToast(getString(R.string.error_server_not_running)); mUtil.showToast(getString(R.string.error_server_not_running));
finish(); finish();
return; return;
} }
mConnection = new SdServiceConnection(getApplicationContext());
Button cancelBtn = Button cancelBtn =
(Button) findViewById(R.id.cancelBtn); (Button) findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel); cancelBtn.setOnClickListener(onCancel);
Button OKBtn = (Button) findViewById(R.id.OKBtn); Button loginBtn = (Button) findViewById(R.id.loginBtn);
OKBtn.setOnClickListener(onOK); loginBtn.setOnClickListener(onLogin);
Button logoutCancelBtn = Button logoutCancelBtn =
(Button) findViewById(R.id.logoutCancelBtn); (Button) findViewById(R.id.logoutCancelBtn);
logoutCancelBtn.setOnClickListener(onCancel); logoutCancelBtn.setOnClickListener(onCancel);
Button logoutBtn = (Button)findViewById(R.id.logoutBtn); Button logoutBtn = (Button) findViewById(R.id.logoutBtn);
logoutBtn.setOnClickListener(onLogout); logoutBtn.setOnClickListener(onLogout);
Button registerBtn = (Button) findViewById(R.id.RegisterBtn);
registerBtn.setOnClickListener(onRegister);
Button resetPasswordBtn = (Button) findViewById(R.id.ResetPasswordBtn);
resetPasswordBtn.setOnClickListener(onResetPassword);
mUnameEt = (EditText) findViewById(R.id.username); // Components required only for osdapi backend
mPasswdEt = (EditText) findViewById(R.id.password); if (LogManager.USE_FIREBASE_BACKEND) { }
//mWac = new WebApiConnection(this, String tokenStr); else {
//mLm = new LogManager(this); mConnection = new SdServiceConnection(getApplicationContext());
Button registerBtn = (Button) findViewById(R.id.RegisterBtn);
registerBtn.setOnClickListener(onRegister);
Button resetPasswordBtn = (Button) findViewById(R.id.ResetPasswordBtn);
resetPasswordBtn.setOnClickListener(onResetPassword);
mUnameEt = (EditText) findViewById(R.id.username);
mPasswdEt = (EditText) findViewById(R.id.password);
}
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 @Override
protected void onStart() { protected void onStart() {
Log.d(TAG, "onStart()"); Log.d(TAG, "onStart()");
super.onStart(); super.onStart();
mUtil.bindToServer(getApplicationContext(), mConnection); if (LogManager.USE_FIREBASE_BACKEND) {
waitForConnection(); updateUi();
} else {
mUtil.bindToServer(getApplicationContext(), mConnection);
waitForConnection();
}
} }
@Override @Override
protected void onStop() { protected void onStop() {
Log.d(TAG, "onStop()"); Log.d(TAG, "onStop()");
super.onStop(); super.onStop();
mUtil.unbindFromServer(getApplicationContext(), mConnection); if (LogManager.USE_FIREBASE_BACKEND) {
}
} else {
mUtil.unbindFromServer(getApplicationContext(), mConnection);
}
}
private void waitForConnection() { private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for // We want the UI to update as soon as it is displayed, but it takes a finite time for
@@ -100,52 +150,65 @@ public class AuthenticateActivity extends AppCompatActivity {
} }
private void initialiseServiceConnection() { private void initialiseServiceConnection() {
Log.v(TAG,"initialiseServiceConnection()");
mLm = mConnection.mSdServer.mLm; mLm = mConnection.mSdServer.mLm;
mWac = mConnection.mSdServer.mLm.mWac; mWac = mConnection.mSdServer.mLm.mWac;
updateUi(); 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) { // Called after the Firebase Auth UI has completed
Log.v(TAG, "Not Logged in - showing log in UI"); private ActivityResultLauncher<Intent> signInLauncher = registerForActivityResult(
loginLl.setVisibility(View.VISIBLE); new FirebaseAuthUIActivityResultContract(),
logoutLl.setVisibility(View.GONE); (result) -> {
} else { 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"); Log.v(TAG, "Already Logged in - showing Log Out prompt");
loginLl.setVisibility(View.GONE); loginLl.setVisibility(View.GONE);
logoutLl.setVisibility(View.VISIBLE); logoutLl.setVisibility(View.VISIBLE);
//TextView tv = (TextView)findViewById(R.id.tokenTv); if (!LogManager.USE_FIREBASE_BACKEND) {
//tv.setText("Logged in with Token: "+storedAuthToken); osdApiLoginLl.setVisibility(View.GONE);
if (mWac != null) { }
mWac.getUserProfile((JSONObject profileObj) -> { mWac.getUserProfile((JSONObject profileObj) -> {
try { try {
Long userId = profileObj.getLong("id"); String userId = profileObj.getString("id");
String userName = profileObj.getString("username"); String userName = profileObj.getString("username");
TextView tv2 = (TextView) findViewById(R.id.userIdTv); TextView tv2 = (TextView) findViewById(R.id.userIdTv);
tv2.setText(userId.toString()); tv2.setText(userId);
tv2 = (TextView) findViewById(R.id.usernameTv); tv2 = (TextView) findViewById(R.id.usernameTv);
tv2.setText(userName); tv2.setText(userName);
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, "Error Parsing profileObj: " + e.getMessage()); Log.e(TAG, "Error Parsing profileObj: " + e.getMessage());
mUtil.showToast("Error Parsing profileObj - this should not happen!!!"); mUtil.showToast("Error Parsing profileObj - this should not happen!!!");
} }
}); });
} else { } 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 = View.OnClickListener onCancel =
@@ -158,45 +221,78 @@ public class AuthenticateActivity extends AppCompatActivity {
} }
}; };
View.OnClickListener onOK = View.OnClickListener onLogin =
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
//m_status=true; //m_status=true;
Log.v(TAG, "onOK()"); if (LogManager.USE_FIREBASE_BACKEND) {
String uname = mUnameEt.getText().toString(); Log.v(TAG, "onLogin() - using Firebase Login");
String passwd = mPasswdEt.getText().toString(); Intent signInIntent = AuthUI.getInstance()
Log.v(TAG,"onOK() - uname="+uname+", passwd="+passwd); .createSignInIntentBuilder()
mWac.authenticate(uname, passwd, new WebApiConnection.StringCallback() { .setAvailableProviders(Arrays.asList(
@Override new AuthUI.IdpConfig.GoogleBuilder().build(),
public void accept(String retVal) { //new AuthUI.IdpConfig.FacebookBuilder().build(),
if (retVal != null) { //new AuthUI.IdpConfig.TwitterBuilder().build(),
Log.d(TAG,"Authentication Success - token is "+retVal); //new AuthUI.IdpConfig.MicrosoftBuilder().build(),
mUtil.showToast("Login Successful"); //new AuthUI.IdpConfig.YahooBuilder().build(),
saveAuthToken(retVal); //new AuthUI.IdpConfig.AppleBuilder().build(),
updateUi(); new AuthUI.IdpConfig.EmailBuilder().build()
} else { //new AuthUI.IdpConfig.PhoneBuilder().build()
Log.e(TAG,"onOk: Authentication failure for "+uname+", "+passwd); //new AuthUI.IdpConfig.AnonymousBuilder().build()))
mUtil.showToast("ERROR: Authentication Failed - Please Try Again"); ))
mUtil.writeToSysLogFile("AuthActivity - Authorisation failed for "+uname+", "+passwd); // ... 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);
mWac.authenticate(uname, passwd, new WebApiConnection.StringCallback() {
@Override
public void accept(String retVal) {
if (retVal != null) {
Log.d(TAG,"Authentication Success - token is "+retVal);
mUtil.showToast("Login Successful");
saveAuthToken(retVal);
updateUi();
} else {
Log.e(TAG,"onOk: Authentication failure for "+uname+", "+passwd);
mUtil.showToast("ERROR: Authentication Failed - Please Try Again");
mUtil.writeToSysLogFile("AuthActivity - Authorisation failed for "+uname+", "+passwd);
}
} }
} });
}); }
//finish();
} }
}; };
View.OnClickListener onLogout = View.OnClickListener onLogout = new View.OnClickListener() {
new View.OnClickListener() { @Override
@Override public void onClick(View view) {
public void onClick(View view) { Log.v(TAG, "onLogout");
Log.v(TAG, "onLogout"); if (LogManager.USE_FIREBASE_BACKEND) {
//m_status=false; AuthUI.getInstance()
mWac.logout(); .signOut(getApplicationContext())
saveAuthToken(null); .addOnCompleteListener(new OnCompleteListener<Void>() {
updateUi(); 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();
}
};
View.OnClickListener onRegister = View.OnClickListener onRegister =
new View.OnClickListener() { new View.OnClickListener() {
@@ -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 HashMap<String, ArrayList<String>> mEventSubTypesHashMap = null;
private String mEventTypeStr = null; private String mEventTypeStr = null;
private String mEventSubTypeStr = null; private String mEventSubTypeStr = null;
private Long mEventId; private String mEventId;
private String mEventNotes = ""; private String mEventNotes = "";
//private Date mEventDateTime; //private Date mEventDateTime;
private RadioGroup mEventTypeRg; private RadioGroup mEventTypeRg;
@@ -59,7 +59,7 @@ public class EditEventActivity extends AppCompatActivity {
Bundle extras = getIntent().getExtras(); Bundle extras = getIntent().getExtras();
if (extras != null) { if (extras != null) {
Long eventId = extras.getLong("eventId"); String eventId = extras.getString("eventId");
mEventId = eventId; mEventId = eventId;
Log.v(TAG, "onCreate - mEventId=" + mEventId); Log.v(TAG, "onCreate - mEventId=" + mEventId);
} }
@@ -68,7 +68,7 @@ public class EditEventActivity extends AppCompatActivity {
Button cancelBtn = Button cancelBtn =
(Button) findViewById(R.id.cancelBtn); (Button) findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel); cancelBtn.setOnClickListener(onCancel);
Button OKBtn = (Button) findViewById(R.id.OKBtn); Button OKBtn = (Button) findViewById(R.id.loginBtn);
OKBtn.setOnClickListener(onOK); OKBtn.setOnClickListener(onOK);
mEventTypeRg = findViewById(R.id.eventTypeRg); mEventTypeRg = findViewById(R.id.eventTypeRg);
@@ -159,10 +159,10 @@ public class EditEventActivity extends AppCompatActivity {
mWac.getEvent(mEventId, new WebApiConnection.JSONObjectCallback() { mWac.getEvent(mEventId, new WebApiConnection.JSONObjectCallback() {
@Override @Override
public void accept(JSONObject eventObj) { public void accept(JSONObject eventObj) {
Log.v(TAG, "onCreate.getEvent"); Log.v(TAG, "initialiseServiceConnection.getEvent");
if (eventObj != null) { if (eventObj != null) {
mEventObj = eventObj; mEventObj = eventObj;
Log.v(TAG, "onCreate.getEvent: eventObj=" + eventObj.toString()); Log.v(TAG, "initialiseServiceConnection.getEvent: eventObj=" + eventObj.toString());
updateUi(); updateUi();
// FIXME: modify updateUi to use mEventObj // FIXME: modify updateUi to use mEventObj
} else { } else {
@@ -198,20 +198,27 @@ public class EditEventActivity extends AppCompatActivity {
try { try {
if (mEventObj != null) { if (mEventObj != null) {
tv = (TextView) findViewById(R.id.eventIdTv); tv = (TextView) findViewById(R.id.eventIdTv);
tv.setText(String.valueOf(mEventObj.getLong("id"))); tv.setText(mEventId);
tv = (TextView) findViewById(R.id.eventAlarmStateTv); 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 = (TextView) findViewById(R.id.eventNotsTv);
tv.setText(mEventObj.getString("desc")); tv.setText(mEventObj.getString("desc"));
tv = (TextView) findViewById(R.id.eventDateTv); tv = (TextView) findViewById(R.id.eventDateTv);
try { try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); String dateStr = mEventObj.getString("dataTime");
Date dataTime = dateFormat.parse(mEventObj.getString("dataTime")); Date dataTime = mUtil.string2date(dateStr);
dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
tv.setText(dateFormat.format(dataTime)); tv.setText(dateFormat.format(dataTime));
} catch (ParseException e) { } catch (Exception e) {
Log.e(TAG,"updateUI: Error Parsing dataDate "+e.getLocalizedMessage()); Log.e(TAG,"updateUI: Error Parsing dataDate "+e.getLocalizedMessage());
tv.setText("---"); tv.setText("---");
} }
@@ -283,12 +290,12 @@ public class EditEventActivity extends AppCompatActivity {
TextView tv = (TextView)findViewById(R.id.eventNotsTv); TextView tv = (TextView)findViewById(R.id.eventNotsTv);
try { try {
mEventObj.put("desc",tv.getText()); 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) { } catch (JSONException e) {
Log.e(TAG,"Error writing mEventObj: "+e.getMessage()); Log.e(TAG,"Error writing mEventObj: "+e.getMessage());
} }
Log.v(TAG, "onOK() - eventObj="+mEventObj.toString()); Log.v(TAG, "onOK() - eventObj="+mEventObj.toString());
try { try {
mWac.updateEvent(mEventObj, new WebApiConnection.JSONObjectCallback() { mWac.updateEvent(mEventObj, new WebApiConnection.JSONObjectCallback() {
@Override @Override
@@ -307,7 +314,7 @@ public class EditEventActivity extends AppCompatActivity {
} }
}); });
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG,"ERROR:"+e.getMessage()); Log.e(TAG,"onOK() - ERROR: "+e.getMessage()+" : " +e.toString());
e.printStackTrace(); e.printStackTrace();
mUtil.showToast("Error Updating Event"); mUtil.showToast("Error Updating Event");
updateUi(); updateUi();

View File

@@ -68,6 +68,7 @@ public class LogManager {
static final private String TAG = "LogManager"; static final private String TAG = "LogManager";
//private String mDbName = "osdData"; //private String mDbName = "osdData";
final static private String mDpTableName = "datapoints"; final static private String mDpTableName = "datapoints";
final static private String mEventsTableName = "events";
private boolean mLogRemote; private boolean mLogRemote;
private boolean mLogRemoteMobile; private boolean mLogRemoteMobile;
private String mAuthToken; private String mAuthToken;
@@ -76,13 +77,15 @@ public class LogManager {
private static Context mContext; private static Context mContext;
private OsdUtil mUtil; private OsdUtil mUtil;
public static WebApiConnection mWac; public static WebApiConnection mWac;
public static final boolean USE_FIREBASE_BACKEND = false;
private boolean mUploadInProgress; private boolean mUploadInProgress;
private long mEventDuration = 120; // event duration in seconds - uploads datapoints that cover this time range centred on the event time. 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) 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 = 60; // Period in seconds between uploads to the remote server.
private ArrayList<JSONObject> mDatapointsToUploadList; private ArrayList<JSONObject> mDatapointsToUploadList;
private int mCurrentEventId; private String mCurrentEventRemoteId;
private long mCurrentEventLocalId = -1;
private int mCurrentDatapointId; private int mCurrentDatapointId;
private long mAutoPrunePeriod = 3600; // Prune the database every hour private long mAutoPrunePeriod = 3600; // Prune the database every hour
private boolean mAutoPruneDb; private boolean mAutoPruneDb;
@@ -121,7 +124,12 @@ public class LogManager {
mUtil = new OsdUtil(mContext, handler); mUtil = new OsdUtil(mContext, handler);
openDb(); openDb();
Log.i(TAG, "Starting Remote Database Interface"); 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); mWac.setStoredToken(mAuthToken);
if (mLogRemote) { if (mLogRemote) {
@@ -162,7 +170,7 @@ public class LogManager {
try { try {
datapoint.put("id", c.getString(c.getColumnIndex("id"))); datapoint.put("id", c.getString(c.getColumnIndex("id")));
datapoint.put("dataTime", c.getString(c.getColumnIndex("dataTime"))); 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("dataJSON", c.getString(c.getColumnIndex("dataJSON")));
datapoint.put("uploaded", c.getString(c.getColumnIndex("uploaded"))); datapoint.put("uploaded", c.getString(c.getColumnIndex("uploaded")));
//Log.v(TAG,"cursor2json() - datapoint="+datapoint.toString()); //Log.v(TAG,"cursor2json() - datapoint="+datapoint.toString());
@@ -177,6 +185,41 @@ public class LogManager {
return dataPointArray.toString(); 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() { private static boolean openDb() {
Log.d(TAG, "openDb"); Log.d(TAG, "openDb");
@@ -187,11 +230,14 @@ public class LogManager {
} else { } else {
Log.i(TAG, "openDb: mOsdDb has been initialised already so not doing anything"); Log.i(TAG, "openDb: mOsdDb has been initialised already so not doing anything");
} }
if (!checkTableExists(mOsdDb, mDpTableName)) { String[] tableNames = new String[]{mDpTableName, mEventsTableName};
Log.e(TAG, "ERROR - Table " + mDpTableName + " does not exist"); for (String tableName : tableNames) {
return false; if (!checkTableExists(mOsdDb, tableName)) {
} else { Log.e(TAG, "ERROR - Table " + tableName + " does not exist");
Log.d(TAG, "table " + mDpTableName + " exists ok"); return false;
} else {
Log.d(TAG, "table " + tableName + " exists ok");
}
} }
} catch (SQLException e) { } catch (SQLException e) {
Log.e(TAG, "Failed to open Database: " + e.toString()); 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! * FIXME - I am sure we should not be using raw SQL Srings to do this!
*/ */
public void writeDatapointToLocalDb(SdData sdData) { public void writeDatapointToLocalDb(SdData sdData) {
Log.v(TAG, "writeDatapointToLocalDb()"); //Log.v(TAG, "writeDatapointToLocalDb()");
Date curDate = new Date(); Date curDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = dateFormat.format(curDate); String dateStr = dateFormat.format(curDate);
String SQLStr = "SQLStr"; String SQLStr = "SQLStr";
if (mOsdDb == null) {
Log.e(TAG, "writeDatapointToLocalDb(): mOsdDb is null - doing nothing");
return;
}
try { try {
//double roiRatio = -1; // Write Datapoint to database
//if (sdData.specPower != 0)
// roiRatio = 10. * sdData.roiPower / sdData.specPower;
SQLStr = "INSERT INTO " + mDpTableName SQLStr = "INSERT INTO " + mDpTableName
+ "(dataTime, status, dataJSON, uploaded)" + "(dataTime, status, dataJSON, uploaded)"
+ " VALUES(" + " VALUES("
@@ -240,20 +288,55 @@ public class LogManager {
+ DatabaseUtils.sqlEscapeString(sdData.toJSON(true)) + "," + DatabaseUtils.sqlEscapeString(sdData.toJSON(true)) + ","
+ 0 + 0
+ ")"; + ")";
if (mOsdDb != null) { mOsdDb.execSQL(SQLStr);
mOsdDb.execSQL(SQLStr); Log.v(TAG, "writeDatapointToLocalDb(): datapoint written to database");
Log.v(TAG, "writeDatapointToLocalDb(): data written to database");
} else {
Log.e(TAG,"writeDatapointToLocalDb(): mOsdDb is null");
}
if (sdData.alarmState != 0) {
Log.i(TAG, "writeDatapointToLocalDb(): adding event to local DB");
createLocalEvent(dateStr, sdData.alarmState);
}
} catch (SQLException e) { } catch (SQLException e) {
Log.e(TAG, "writeToLocalDb(): Error Writing Data: " + e.toString()); Log.e(TAG, "writeToLocalDb(): Error Writing Data: " + e.toString());
Log.e(TAG, "SQLStr was " + SQLStr); Log.e(TAG, "SQLStr was " + SQLStr);
} catch (NullPointerException e) { } catch (NullPointerException e) {
Log.e(TAG, "writeToLocalDb(): Null Pointer Exception: " + e.toString()); 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; String retVal;
try { try {
String selectStr = "select * from " + mDpTableName + " where id=" + id + ";"; 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); c = mOsdDb.rawQuery(selectStr, null);
retVal = cursor2Json(c); retVal = cursor2Json(c);
} catch (Exception e) { } catch (Exception e) {
@@ -279,7 +359,6 @@ public class LogManager {
retVal = null; retVal = null;
} }
return (retVal); 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. * @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. * @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); 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(); ContentValues cv = new ContentValues();
cv.put("uploaded", eventId); cv.put("uploaded", eventId);
int nRowsUpdated = mOsdDb.update(mDpTableName, cv, "id = ?", int nRowsUpdated = mOsdDb.update(mDpTableName, cv, "id = ?",
new String[]{String.format("%d", id)}); new String[]{String.format("%d", id)});
return (nRowsUpdated == 1); return (nRowsUpdated == 1);
} }
/** /**
@@ -356,7 +437,7 @@ public class LogManager {
String whereClause = getEventWhereClause(includeWarnings); String whereClause = getEventWhereClause(includeWarnings);
//sqlStr = "SELECT * from " + mDbTableName + " where Status in (" + statusListStr + ") order by dataTime desc;"; //sqlStr = "SELECT * from " + mDbTableName + " where Status in (" + statusListStr + ") order by dataTime desc;";
String[] columns = {"*"}; String[] columns = {"*"};
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs, new SelectQueryTask(mEventsTableName, columns, whereClause, whereArgs,
null, null, "dataTime DESC", (Cursor cursor) -> { null, null, "dataTime DESC", (Cursor cursor) -> {
Log.v(TAG, "getEventsList - returned " + cursor); Log.v(TAG, "getEventsList - returned " + cursor);
if (cursor != null) { if (cursor != null) {
@@ -365,7 +446,7 @@ public class LogManager {
HashMap<String, String> event = new HashMap<>(); HashMap<String, String> event = new HashMap<>();
//event.put("id", cursor.getString(cursor.getColumnIndex("id"))); //event.put("id", cursor.getString(cursor.getColumnIndex("id")));
event.put("dataTime", cursor.getString(cursor.getColumnIndex("dataTime"))); 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); String statusStr = mUtil.alarmStatusToString(status);
event.put("status", statusStr); event.put("status", statusStr);
event.put("uploaded", cursor.getString(cursor.getColumnIndex("uploaded"))); event.put("uploaded", cursor.getString(cursor.getColumnIndex("uploaded")));
@@ -385,25 +466,48 @@ public class LogManager {
*/ */
public int pruneLocalDb() { public int pruneLocalDb() {
Log.d(TAG, "pruneLocalDb()"); Log.d(TAG, "pruneLocalDb()");
int retVal; int retVal = 0;
long currentDateMillis = new Date().getTime(); long currentDateMillis = new Date().getTime();
long endDateMillis = currentDateMillis - 24 * 3600 * 1000 * mDataRetentionPeriod; long endDateMillis = currentDateMillis - 24 * 3600 * 1000 * mDataRetentionPeriod;
//long endDateMillis = currentDateMillis - 3600*1000* mDataRetentionPeriod; // Using hours rather than days for testing //long endDateMillis = currentDateMillis - 3600*1000* mDataRetentionPeriod; // Using hours rather than days for testing
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endDateStr = dateFormat.format(new Date(endDateMillis)); String endDateStr = dateFormat.format(new Date(endDateMillis));
try { String[] tableNames = new String[]{mDpTableName, mEventsTableName};
String selectStr = "DataTime<=?"; for (String tableName : tableNames) {
String[] selectArgs = {endDateStr}; Log.i(TAG, "pruneLocalDb - pruning table " + tableName);
retVal = mOsdDb.delete(mDpTableName, selectStr, selectArgs); try {
} catch (Exception e) { String selectStr = "DataTime<=?";
Log.d(TAG, "Error deleting datapoints" + e.toString()); String[] selectArgs = {endDateStr};
retVal = 0; retVal = mOsdDb.delete(tableName, selectStr, selectArgs);
} catch (Exception e) {
Log.d(TAG, "Error deleting data " + e.toString());
retVal = 0;
}
Log.d(TAG, String.format("pruneLocalDb() - deleted %d records from table %s", retVal, tableName));
} }
Log.d(TAG, String.format("pruneLocalDb() - deleted %d records", retVal));
return (retVal); 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. * 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; long endDateMillis = currentDateMillis - 1000 * mEventDuration;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endDateStr = dateFormat.format(new Date(endDateMillis)); String endDateStr = dateFormat.format(new Date(endDateMillis));
String whereClauseUploaded = "uploaded = 0"; String whereClauseUploaded = "uploaded is null";
String whereClauseDate = "DataTime<?"; String whereClauseDate = "DataTime<?";
String whereClause = whereClauseStatus + " AND " + whereClauseUploaded + " AND " + whereClauseDate; String whereClause = whereClauseStatus + " AND " + whereClauseUploaded + " AND " + whereClauseDate;
@@ -432,9 +536,8 @@ public class LogManager {
whereArgs[i] = whereArgsStatus[i]; whereArgs[i] = whereArgsStatus[i];
} }
whereArgs[whereArgsStatus.length] = endDateStr; whereArgs[whereArgsStatus.length] = endDateStr;
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs, new SelectQueryTask(mEventsTableName, columns, whereClause, whereArgs,
null, null, "dataTime DESC", (Cursor cursor) -> { null, null, "dataTime DESC", (Cursor cursor) -> {
Log.v(TAG, "getEventsList - returned " + cursor);
Long recordId = new Long(-1); Long recordId = new Long(-1);
if (cursor != null) { if (cursor != null) {
Log.v(TAG, "getNextEventToUpload - returned " + cursor.getCount() + " records"); Log.v(TAG, "getNextEventToUpload - returned " + cursor.getCount() + " records");
@@ -443,9 +546,8 @@ public class LogManager {
Log.v(TAG, "getNextEventToUpload() - no events to Upload - exiting"); Log.v(TAG, "getNextEventToUpload() - no events to Upload - exiting");
recordId = new Long(-1); recordId = new Long(-1);
} else { } else {
String recordStr = cursor.getString(3);
recordId = cursor.getLong(0); recordId = cursor.getLong(0);
Log.d(TAG, "getNextEventToUpload(): id=" + recordId + ", recordStr=" + recordStr); Log.d(TAG, "getNextEventToUpload(): id=" + recordId);
} }
} }
callback.accept(recordId); callback.accept(recordId);
@@ -494,13 +596,13 @@ public class LogManager {
* @return True on successful start or false if call fails. * @return True on successful start or false if call fails.
*/ */
public boolean getLocalEventsCount(boolean includeWarnings, WebApiConnection.LongCallback callback) { 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[] whereArgs = getEventWhereArgs(includeWarnings);
String whereClause = getEventWhereClause(includeWarnings); String whereClause = getEventWhereClause(includeWarnings);
String[] columns = {"*"}; String[] columns = {"*"};
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs, new SelectQueryTask(mEventsTableName, columns, whereClause, whereArgs,
null, null, null, (Cursor cursor) -> { null, null, null, (Cursor cursor) -> {
Log.v(TAG, "getLocalEventsCount - returned " + cursor); //Log.v(TAG, "getLocalEventsCount - returned " + cursor);
Long eventCount = Long.valueOf(0); Long eventCount = Long.valueOf(0);
if (cursor != null) { if (cursor != null) {
eventCount = Long.valueOf(cursor.getCount()); eventCount = Long.valueOf(cursor.getCount());
@@ -517,13 +619,13 @@ public class LogManager {
* @return True on successful start or false if call fails. * @return True on successful start or false if call fails.
*/ */
public boolean getLocalDatapointsCount(WebApiConnection.LongCallback callback) { public boolean getLocalDatapointsCount(WebApiConnection.LongCallback callback) {
Log.v(TAG, "getLocalDatapointsCount"); //Log.v(TAG, "getLocalDatapointsCount");
String[] whereArgs = null; String[] whereArgs = null;
String whereClause = null; String whereClause = null;
String[] columns = {"*"}; String[] columns = {"*"};
new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs, new SelectQueryTask(mDpTableName, columns, whereClause, whereArgs,
null, null, null, (Cursor cursor) -> { null, null, null, (Cursor cursor) -> {
Log.v(TAG, "getLocalDatapointsCount - returned " + cursor); //Log.v(TAG, "getLocalDatapointsCount - returned " + cursor);
Long eventCount = Long.valueOf(0); Long eventCount = Long.valueOf(0);
if (cursor != null) { if (cursor != null) {
eventCount = Long.valueOf(cursor.getCount()); eventCount = Long.valueOf(cursor.getCount());
@@ -568,7 +670,7 @@ public class LogManager {
@Override @Override
protected Cursor doInBackground(Void... params) { 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) Log.v(TAG, "SelectQueryTask.doInBackground: mTable=" + mTable + ", mColumns=" + Arrays.toString(mColumns)
+ ", mSelection=" + mSelection + ", mSelectionArgs=" + Arrays.toString(mSelectionArgs) + ", mGroupBy=" + mGroupBy + ", mSelection=" + mSelection + ", mSelectionArgs=" + Arrays.toString(mSelectionArgs) + ", mGroupBy=" + mGroupBy
+ ", mHaving =" + mHaving + ", mOrderBy=" + mOrderBy); + ", mHaving =" + mHaving + ", mOrderBy=" + mOrderBy);
@@ -659,17 +761,24 @@ public class LogManager {
*/ */
public void uploadSdData() { public void uploadSdData() {
//int eventId = -1; //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. // First try uploading full alarms, and only if we do not have any of those, upload warnings.
boolean warningsArr[] = { false, true }; //boolean warningsArr[] = {false, true};
for (int n=0; n<warningsArr.length; n++) { // 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]; boolean warningsVal = warningsArr[n];
Log.i(TAG, "uploadSdData(): warningsVal=" + warningsVal); 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) -> { getNextEventToUpload(warningsVal, (Long eventId) -> {
if (eventId != -1) { if (eventId != -1) {
Log.v(TAG, "uploadSdData() - eventId=" + eventId); Log.i(TAG, "uploadSdData() - next Event to Upload eventId=" + eventId);
String eventJsonStr = getDatapointById(eventId); String eventJsonStr = getLocalEventById(eventId);
Log.v(TAG, "uploadSdData() - eventJsonStr=" + eventJsonStr); Log.v(TAG, "uploadSdData() - event to upload eventJsonStr=" + eventJsonStr);
//int eventType; //int eventType;
JSONObject eventObj; JSONObject eventObj;
int eventAlarmStatus; 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. eventObj = datapointJsonArr.getJSONObject(0); // We only look at the first (and hopefully only) item in the array.
eventAlarmStatus = Integer.parseInt(eventObj.getString("status")); eventAlarmStatus = Integer.parseInt(eventObj.getString("status"));
eventDateStr = eventObj.getString("dataTime"); 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); + eventAlarmStatus + ", eventDateStr=" + eventDateStr);
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, "ERROR parsing event JSON Data" + eventJsonStr); Log.e(TAG, "uploadSdData(): ERROR parsing event JSON Data" + eventJsonStr);
e.printStackTrace(); e.printStackTrace();
return; return;
} catch (NullPointerException e) { } 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(); e.printStackTrace();
return; return;
} }
try { try {
eventDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(eventDateStr); eventDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(eventDateStr);
} catch (ParseException e) { } catch (ParseException e) {
Log.e(TAG, "Error parsing date " + eventDateStr); Log.e(TAG, "UploadSdData(): Error parsing date " + eventDateStr);
return; return;
} }
Log.i(TAG, "uploadSdData - calling mWac.createEvent");
mCurrentEventLocalId = eventId;
mWac.createEvent(eventAlarmStatus, eventDate, "", this::createEventCallback); mWac.createEvent(eventAlarmStatus, eventDate, "", this::createEventCallback);
} else { } 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. // started if necessary.
public void finishUpload() { public void finishUpload() {
mCurrentEventId = -1; mCurrentEventRemoteId = null;
mCurrentEventLocalId = -1;
mCurrentDatapointId = -1;
mDatapointsToUploadList = null; mDatapointsToUploadList = null;
mUploadInProgress = false; mUploadInProgress = false;
} }
@@ -717,82 +832,97 @@ public class LogManager {
// Called by WebApiConnection when a new event record is created. // 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 // 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. // and uploads those as a batch of data points.
public void createEventCallback(String eventStr) { public void createEventCallback(String eventId) {
Log.v(TAG, "eventCallback(): " + eventStr); Log.v(TAG, "createEventCallback(): " + eventId);
Date eventDate; Log.v(TAG, "createEventCallback(): Retrieving remote event details");
String eventDateStr; mWac.getEvent(eventId, new WebApiConnection.JSONObjectCallback() {
int eventId; @Override
try { public void accept(JSONObject eventObj) {
JSONObject eventObj = new JSONObject(eventStr); if (eventObj == null) {
eventDateStr = eventObj.getString("dataTime"); Log.e(TAG,"createEventCallback() - eventObj is null - failed to create event");
eventId = eventObj.getInt("id"); mUtil.showToast("Error Creating Remote Event");
} catch (JSONException e) { } else {
Log.e(TAG, "eventCallback() - Error parsing eventStr: " + eventStr); Log.v(TAG, "createEventCallback() - eventObj=" + eventObj.toString());
finishUpload(); Date eventDate;
return; String eventDateStr = "";
}
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);
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");
getDatapointsByDate(
dateFormat.format(new Date(startDateMillis)),
dateFormat.format(new Date(endDateMillis)), (String datapointsJsonStr) -> {
Log.v(TAG, "eventCallback() - datapointsJsonStr=" + datapointsJsonStr);
JSONArray dataObj;
mDatapointsToUploadList = new ArrayList<JSONObject>();
try { try {
//DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); String dateStr= eventObj.getString("dataTime");
dataObj = new JSONArray(datapointsJsonStr); eventDate = mUtil.string2date(dateStr);
for (int i = 0; i < dataObj.length(); i++) {
mDatapointsToUploadList.add(dataObj.getJSONObject(i));
}
} catch (JSONException e) { } catch (JSONException e) {
Log.v(TAG, "Error Creating JSON Object from string " + datapointsJsonStr); Log.e(TAG, "createEventCallback() - Error parsing JSONObject: " + eventObj.toString());
dataObj = null; finishUpload();
return;
}
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 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, "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, "createEventCallback(): Error Creating JSON Object from string " + datapointsJsonStr);
dataObj = null;
finishUpload();
}
// This starts the process of uploading the datapoints, one at a time.
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(); 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);
uploadNextDatapoint();
});
} }
// takes the next datapoint of the list mDatapointsToUploadList and uploads it to the remote server. // takes the next datapoint of the list mDatapointsToUploadList and uploads it to the remote server.
// datapointCallback is called when the upload is complete. // datapointCallback is called when the upload is complete.
public void uploadNextDatapoint() { public void uploadNextDatapoint() {
Log.v(TAG, "uploadDatapoint()"); //Log.v(TAG, "uploadNextDatapoint()");
if (mDatapointsToUploadList.size() > 0) { if (mDatapointsToUploadList != null) {
mUploadInProgress = true; if (mDatapointsToUploadList.size() > 0) {
try { mUploadInProgress = true;
mCurrentDatapointId = mDatapointsToUploadList.get(0).getInt("id"); try {
} catch (JSONException e) { mCurrentDatapointId = mDatapointsToUploadList.get(0).getInt("id");
Log.e(TAG, "Error reading currentDatapointID from mDatapointsToUploadList[0]" + e.getMessage()); } catch (JSONException e) {
Log.e(TAG, "Removing mDatapointsToUploadList[0] and trying the next datapoint"); Log.e(TAG, "uploadNextDatapoint(): Error reading currentDatapointID from mDatapointsToUploadList[0]" + e.getMessage());
mDatapointsToUploadList.remove(0); Log.e(TAG, "uploadNextDatapoint(): Removing mDatapointsToUploadList[0] and trying the next datapoint");
uploadNextDatapoint(); mDatapointsToUploadList.remove(0);
uploadNextDatapoint();
}
Log.v(TAG, "uploadNextDatapoint() - " + mDatapointsToUploadList.size() + " datapoints to upload. Uploading datapoint ID:" + mCurrentDatapointId);
mWac.createDatapoint(mDatapointsToUploadList.get(0), mCurrentEventRemoteId, this::datapointCallback);
} else {
Log.i(TAG, "uploadNextDatapoint() - All datapoints uploaded!");
setEventToUploaded(mCurrentEventLocalId, mCurrentEventRemoteId);
finishUpload();
} }
Log.v(TAG, "uploadDatapoint() - uploading datapoint with local id of " + mCurrentDatapointId);
mWac.createDatapoint(mDatapointsToUploadList.get(0), mCurrentEventId, this::datapointCallback);
} else { } else {
mCurrentEventId = -1; Log.w(TAG,"uploadNextDatapoint - mDatapointsToUploadList is null - I don't thin this should have happened!");
mCurrentDatapointId = -1;
mUploadInProgress = false;
} }
} }
@@ -800,11 +930,15 @@ public class LogManager {
// a datapoint based on mDatapointsToUploadList(0) so removes that from the list and calls UploadDatapoint() // a datapoint based on mDatapointsToUploadList(0) so removes that from the list and calls UploadDatapoint()
// to upload the next one. // to upload the next one.
public void datapointCallback(String datapointStr) { 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.size() > 0) { if (mDatapointsToUploadList != null) {
mDatapointsToUploadList.remove(0); if (mDatapointsToUploadList.size() > 0) {
mDatapointsToUploadList.remove(0);
}
} else {
Log.w(TAG,"datapointCallback - mDatapointsToUploadList is null - I don't thin this should have happened!");
} }
setDatapointToUploaded(mCurrentDatapointId, mCurrentEventId); setDatapointToUploaded(mCurrentDatapointId, mCurrentEventRemoteId);
uploadNextDatapoint(); uploadNextDatapoint();
} }
@@ -903,9 +1037,19 @@ public class LogManager {
String SQLStr = "CREATE TABLE IF NOT EXISTS " + mDpTableName + "(" String SQLStr = "CREATE TABLE IF NOT EXISTS " + mDpTableName + "("
+ "id INTEGER PRIMARY KEY," + "id INTEGER PRIMARY KEY,"
+ "dataTime DATETIME," + "dataTime DATETIME,"
+ "Status INT," + "status INT,"
+ "dataJSON TEXT," + "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); db.execSQL(SQLStr);
} }

View File

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

View File

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

View File

@@ -59,6 +59,7 @@ import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; 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; public final int ALARM_STATUS_WARNING = 1;

View File

@@ -17,6 +17,7 @@ import android.widget.DatePicker;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TimePicker; import android.widget.TimePicker;
import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
/** /**
@@ -40,6 +41,7 @@ public class ReportSeizureActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()"); Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mContext = this;
mUtil = new OsdUtil(this, serverStatusHandler); mUtil = new OsdUtil(this, serverStatusHandler);
if (!mUtil.isServerRunning()) { if (!mUtil.isServerRunning()) {
mUtil.showToast(getString(R.string.error_server_not_running)); mUtil.showToast(getString(R.string.error_server_not_running));
@@ -65,7 +67,7 @@ public class ReportSeizureActivity extends AppCompatActivity {
//mLm= new LogManager(mContext); //mLm= new LogManager(mContext);
Button okBtn = Button okBtn =
(Button) findViewById(R.id.OKBtn); (Button) findViewById(R.id.loginBtn);
okBtn.setOnClickListener(onOk); okBtn.setOnClickListener(onOk);
Button cancelBtn = Button cancelBtn =
@@ -142,22 +144,12 @@ public class ReportSeizureActivity extends AppCompatActivity {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Log.v(TAG, "onOk"); 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); String dateStr=String.format("%4d-%02d-%02d %02d:%02d:30",mYear,mMonth+1,mDay, mHour, mMinute);
Log.v(TAG, "onOk() - dateSTr="+dateStr); Log.v(TAG, "onOk() - dateSTr="+dateStr);
mMsg = "Finding Nearest Datapoint to Date/Time "+dateStr+"..."; mLm.createLocalEvent(dateStr,5);
mLm.getNearestDatapointToDate(dateStr, (Long id) -> { mUtil.showToast("Seizure Event Created");
mMsg = mMsg + "\nNearest Datapoint is "+id; finish();
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));
finish();
} else {
mMsg = mMsg + "\n*** Datapoint not found - not doing anything ***";
mUtil.showToast(getString(R.string.DatapointNotFound));
}
});
} }
}; };
View.OnClickListener onCancel = 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 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. 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) 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 long mAutoPrunePeriod = 3600; // Prune the database every hour
private boolean mAutoPruneDb; private boolean mAutoPruneDb;
@@ -1137,7 +1137,7 @@ public class SdServer extends Service implements SdDataReceiver {
//writeToSD(); //writeToSD();
mLm.writeDatapointToLocalDb(mSdData); mLm.writeDatapointToLocalDb(mSdData);
} else { } 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"); //prefVal = SP.getString("RemoteLogPeriod", "60");
//mRemoteLogPeriod = Integer.parseInt(prefVal); //mRemoteLogPeriod = Integer.parseInt(prefVal);
mRemoteLogPeriod = 60; //mRemoteLogPeriod = 60;
Log.v(TAG, "mRemoteLogPeriod=" + mRemoteLogPeriod); Log.v(TAG, "mRemoteLogPeriod=" + mRemoteLogPeriod);
//mOSDUname = SP.getString("OSDUname", "<username>"); //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."); Log.i(TAG, "SmsTimer.onFinish() - Last Location is Null so sending first SMS without location.");
} }
} else { } else {
Log.e(TAG,"SmsTImer.onFinish() - mLocationFinder is NULL - this should not happen!"); Log.e(TAG,"SmsTimer.onFinish - mLocationFinder is null - this should not happen!");
mUtil.showToast("Error Finding Location - mLocationFinder is null - please report this issue!"); 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"); Log.i(TAG, "SmsTimer.onFinish() - Sending to " + mSMSNumbers.length + " Numbers");
mUtil.writeToSysLogFile("SdServer.SmsTimer.onFinish()"); mUtil.writeToSysLogFile("SdServer.SmsTimer.onFinish()");
@@ -1555,28 +1555,26 @@ public class SdServer extends Service implements SdDataReceiver {
// Retrieve events from remote database // Retrieve events from remote database
if (mLm.mWac.getEvents((JSONObject remoteEventsObj) -> { if (mLm.mWac.getEvents((JSONObject remoteEventsObj) -> {
Log.v(TAG, "CheckEvents.getEvents.Callback()"); Log.v(TAG, "CheckEvents.getEvents.Callback()");
long firstUnvalidatedEvent; Boolean haveUnvalidatedEvent = false;
if (remoteEventsObj == null) { if (remoteEventsObj == null) {
Log.e(TAG, "CheckEvents.Callback: Error Retrieving events"); Log.e(TAG, "CheckEvents.Callback: Error Retrieving events");
} else { } else {
try { try {
JSONArray eventsArray = remoteEventsObj.getJSONArray("events"); JSONArray eventsArray = remoteEventsObj.getJSONArray("events");
// A bit of a hack to display in reverse chronological order // A bit of a hack to display in reverse chronological order
firstUnvalidatedEvent = -1;
for (int i = eventsArray.length() - 1; i >= 0; i--) { for (int i = eventsArray.length() - 1; i >= 0; i--) {
JSONObject eventObj = eventsArray.getJSONObject(i); JSONObject eventObj = eventsArray.getJSONObject(i);
Long id = eventObj.getLong("id");
String typeStr = eventObj.getString("type"); String typeStr = eventObj.getString("type");
//Log.v(TAG,"CheckEventsTimer: id="+id+", typeStr="+typeStr); //Log.v(TAG,"CheckEventsTimer: id="+id+", typeStr="+typeStr);
if (typeStr.equals("null")) { if (typeStr.equals("null") || typeStr.equals("")) {
firstUnvalidatedEvent = id; haveUnvalidatedEvent = true;
//Log.v(TAG,"CheckEventsTimer:setting firstUnvalidatedEvent to "+firstUnvalidatedEvent); //Log.v(TAG,"CheckEventsTimer:setting firstUnvalidatedEvent to "+firstUnvalidatedEvent);
} }
} }
Log.v(TAG, "CheckEventsTimer.onFinish.callback - firstUnvalidatedEvent = " + Log.v(TAG, "CheckEventsTimer.onFinish.callback - haveUnvalidatedEvent = " +
firstUnvalidatedEvent); haveUnvalidatedEvent);
if (firstUnvalidatedEvent >= 0) { if (haveUnvalidatedEvent) {
showEventNotification(firstUnvalidatedEvent); showEventNotification();
mNM.cancel(DATASHARE_NOTIFICATION_ID); mNM.cancel(DATASHARE_NOTIFICATION_ID);
} else { } else {
mNM.cancel(EVENT_NOTIFICATION_ID); 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. * Show a notification to tell the user that we have unvalidated events.
*/ */
private void showEventNotification(long eventId) { private void showEventNotification() {
Log.v(TAG, "showEventNotification()"); Log.v(TAG, "showEventNotification()");
int iconId; int iconId;
String titleStr; String titleStr;

View File

@@ -4,6 +4,8 @@ import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import com.android.volley.AuthFailureError; import com.android.volley.AuthFailureError;
import com.android.volley.Request; import com.android.volley.Request;
import com.android.volley.RequestQueue; import com.android.volley.RequestQueue;
@@ -12,6 +14,18 @@ import com.android.volley.VolleyError;
import com.android.volley.VolleyLog; import com.android.volley.VolleyLog;
import com.android.volley.toolbox.StringRequest; import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley; 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.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@@ -22,20 +36,19 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
// This class is intended to handle all interactions with the OSD WebAPI // This class is intended to handle all interactions with the OSD WebAPI
public class WebApiConnection { public abstract class WebApiConnection {
public String retVal; protected Context mContext;
public int retCode; protected OsdUtil mUtil;
public boolean mServerConnectionOk = false;
private String mUrlBase = "https://osdApi.ddns.net";
private String TAG = "WebApiConnection"; private String TAG = "WebApiConnection";
private String mAuthToken; private String mAuthToken;
private Context mContext;
private OsdUtil mUtil;
RequestQueue mQueue;
public interface JSONObjectCallback { public interface JSONObjectCallback {
public void accept(JSONObject retValObj); public void accept(JSONObject retValObj);
@@ -51,237 +64,23 @@ public class WebApiConnection {
public WebApiConnection(Context context) { public WebApiConnection(Context context) {
mContext = context; mContext = context;
mQueue = Volley.newRequestQueue(context);
mUtil = new OsdUtil(mContext, new Handler()); mUtil = new OsdUtil(mContext, new Handler());
} }
public void close() { public void close() {
Log.i(TAG,"stop()"); Log.i(TAG, "stop()");
mQueue.stop();
} }
/** public abstract boolean isLoggedIn();
* 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);
}
}
// Create a new event in the remote database, based on the provided parameters. // 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) { // passes the newly created documentId to function callback on successful completion, or null on error.
Log.v(TAG, "createEvent()"); public abstract boolean createEvent(final int osdAlarmState, final Date eventDate, final String eventDesc, StringCallback callback);
String urlStr = mUrlBase + "/api/events/";
Log.v(TAG, "urlStr=" + urlStr);
final String authtoken = getStoredToken();
if (!isLoggedIn()) { // calls function callback with a JSONObject representation of the event with id 'eventId'
Log.v(TAG, "not logged in - doing nothing"); public abstract boolean getEvent(String eventId, JSONObjectCallback callback);
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, "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 * 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 * @param callback
* @return true on success or false on failure to initiate the request. * @return true on success or false on failure to initiate the request.
*/ */
public boolean getEvents(JSONObjectCallback callback) { public abstract 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 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. * 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. * @return true if request sent successfully or else false.
*/ */
public boolean getEventTypes(JSONObjectCallback callback) { public abstract 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. * Retrieve a trivial file from the server to check we have a good server connection.
* sets mServerConnectionOk. * sets mServerConnectionOk.
*
* @return true if request sent successfully or else false. * @return true if request sent successfully or else false.
*/ */
public boolean checkServerConnection() { public abstract 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, public abstract boolean getUserProfile(JSONObjectCallback callback);
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);
public boolean authenticate(final String uname, final String passwd, StringCallback callback) {
Log.e(TAG,"WebApiConnection.authenticate(username, password, callback) Not Implemented");
return false;
}
// Remove the stored token so future calls are not authenticated.
public void logout() {
Log.v(TAG, "logout()");
setStoredToken(null);
}
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:layout_height="wrap_content"
android:orientation="vertical"> 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 <EditText
android:id="@+id/username" android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -53,30 +83,12 @@
android:hint="password" android:hint="password"
android:inputType="textPassword" /> 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" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal"
>
<Button <Button
android:id="@+id/RegisterBtn" android:id="@+id/RegisterBtn"
@@ -94,6 +106,8 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/logout_ui" android:id="@+id/logout_ui"
android:layout_width="fill_parent" android:layout_width="fill_parent"
@@ -117,8 +131,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"
android:text="userId" /> android:text="userId" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
<TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"
@@ -139,12 +159,6 @@
</LinearLayout> </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" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -164,40 +178,29 @@
android:layout_weight="1" android:layout_weight="1"
android:text="@string/logout" /> android:text="@string/logout" />
</LinearLayout> </LinearLayout>
<!-- </LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/aboutDataSharingBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:layout_weight="1"
android:text="@string/about_data_sharing" />
<Button <Button
android:id="@+id/createEventBtn" android:id="@+id/privacyPolicyBtn"
android:layout_width="wrap_content" 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"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:layout_weight="1"
android:text="@string/privacy_policy" />
<Button
android:id="@+id/getLocalEventsBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="getLocalEvents" />
</LinearLayout>
-->
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

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

View File

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

View File

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

View File

@@ -5,71 +5,67 @@
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> 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 <LinearLayout
android:layout_width="fill_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/event_date_remote_tv" android:id="@+id/event_date_remote_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold" android:text="date"
android:text="date" /> android:textStyle="bold" />
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:id="@+id/event_type_remote_tv" android:id="@+id/event_type_remote_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold" android:text="---"
android:text="---" /> android:textStyle="bold" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text=" : " /> android:text=" : " />
<TextView <TextView
android:id="@+id/event_subtype_remote_tv" android:id="@+id/event_subtype_remote_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="---" /> android:text="---" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/event_alarmState_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alarm" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="notes"
android:textStyle="italic"
android:id="@+id/event_notes_remote_tv" />
<!-- <TextView <TextView
android:layout_width="wrap_content" android:id="@+id/event_alarmState_remote_tv"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:text="dataJSON" android:layout_height="wrap_content"
android:id="@+id/event_dataJSON" /> 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" />
<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/textAppearanceSmall" />
android:text="eventId" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -3,6 +3,8 @@
<string name="app_name">OpenSeizureDetector</string> <string name="app_name">OpenSeizureDetector</string>
<string name="changelog"> <string name="changelog">
"\n "\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.1 - fixed crash in Report Seizure function
\nV4.0.0 \nV4.0.0
- Introduced the &lt;b>Data Sharing&lt;/b> feature to allow users to share their seizure and false alarm data - 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="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="error_server_not_running">ERROR: OpenSeizureDetector Server is not running - please re-start it</string>
<string name="system_logs">System Logs</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_text">Select for more information</string>
<string name="datasharing_notification_title">OpenSeizureDetector Data Sharing Problem</string> <string name="datasharing_notification_title">OpenSeizureDetector Data Sharing Problem</string>
<string name="datasharing_about_title">OpenSeizureDetector Data Sharing</string> <string name="datasharing_about_title">OpenSeizureDetector Data Sharing</string>
@@ -430,4 +432,6 @@
and selecting <b>Apps->OpenSeizureDetector->Permissions</b>. and selecting <b>Apps->OpenSeizureDetector->Permissions</b>.
</string> </string>
<string name="permissions_required">Permissions Disclosure</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> </resources>

View File

@@ -15,20 +15,14 @@ import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(sdk = {Build.VERSION_CODES.O_MR1}, packageName = "uk.org.openseizuredetector") @Config(sdk = {Build.VERSION_CODES.O_MR1}, packageName = "uk.org.openseizuredetector")
public class LogManagerTest extends TestCase { 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 { public void tearDown() throws Exception {
mLm.close();
} }
SdData getFakeSdData() { SdData getFakeSdData() {
SdData sdData = new SdData(); return null;
return sdData;
} }

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