Merge branch 'Logging' into main - V4.0.6

This commit is contained in:
Graham Jones
2022-06-05 20:20:23 +01:00
188 changed files with 8326 additions and 2451 deletions

View File

@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 28
compileSdkVersion 31
useLibrary 'org.apache.http.legacy'
defaultConfig {
applicationId "uk.org.openseizuredetector"
minSdkVersion 21
targetSdkVersion 29
minSdkVersion 23
targetSdkVersion 30
multiDexEnabled true
}
@@ -24,30 +24,41 @@ android {
includeAndroidResources = true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'com.android.support:multidex:1.0.3'
implementation 'androidx.multidex:multidex:2.0.1'
implementation files('libs/mpandroidchartlibrary-2-0-7.jar')
implementation 'com.getpebble:pebblekit:3.1.0@aar'
// Unit testing dependencies
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.android.material:material:1.4.0'
implementation 'com.google.firebase:firebase-auth:19.2.0'
implementation 'androidx.test:core:1.4.0'
testImplementation 'junit:junit:4.13.2'
// Set this dependency if you want to use Mockito
testImplementation 'org.mockito:mockito-core:1.10.19'
testImplementation 'org.mockito:mockito-core:4.3.1'
// Set this dependency if you want to use Hamcrest matching
testImplementation 'org.hamcrest:hamcrest-library:1.1'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
//compile files('libs/JTransforms-3.1-with-dependencies.jar')
testImplementation 'org.hamcrest:hamcrest-library:2.2'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'org.apache.commons:commons-math3:3.6.1'
// google play services used for location finding for SMS alerts.
implementation 'com.google.android.gms:play-services:10.0.1'
implementation 'com.github.wendykierp:JTransforms:3.0'
implementation 'com.google.android.gms:play-services-location:10.0.0'
//implementation 'com.google.android.gms:play-services:+'
implementation 'com.google.android.gms:play-services-wearable:+'
implementation 'com.github.wendykierp:JTransforms:3.1'
implementation 'com.google.android.gms:play-services-location:+'
//implementation 'com.github.RohitSurwase.UCE-Handler:uce_handler:1.3'
testImplementation 'org.robolectric:robolectric:4.3'
testImplementation 'org.robolectric:robolectric:4.7.3'
implementation 'com.android.volley:volley:1.2.1'
implementation platform('com.google.firebase:firebase-bom:29.2.0')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.firebaseui:firebase-ui-auth:7.2.0'
implementation 'com.google.firebase:firebase-firestore'
}
repositories {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,18 +0,0 @@
{
"version": 2,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "uk.org.openseizuredetector",
"variantName": "processReleaseResources",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 87,
"versionName": "3.6.2",
"outputFile": "app-release.apk"
}
]
}

View File

@@ -1 +0,0 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":72,"versionName":"3.5.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]

View File

@@ -2,21 +2,23 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="uk.org.openseizuredetector"
android:versionCode="87"
android:versionName="3.6.2">
android:versionCode="102"
android:versionName="4.0.6">
<!-- android:allowBackup="false" -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
@@ -24,16 +26,19 @@
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />
<application
android:icon="@drawable/star_of_life_48x48"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme"
> <!--@android:style/Theme.Holo.Light"-->
<activity android:name=".BLEScanActivity"></activity>
<activity android:name=".DBQueryActivity"></activity>
<!-- android:usesCleartextTraffic="true" -->
android:theme="@style/AppTheme">
<activity android:name=".AuthenticateActivity"/>
<!-- @android:style/Theme.Holo.Light" -->
<activity android:name=".BLEScanActivity" />
<activity android:name=".ExportDataActivity" /> <!-- android:usesCleartextTraffic="true" -->
<activity android:name=".StartupActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -57,7 +62,10 @@
android:name=".SdServer"
android:exported="false" />
<activity android:name=".LogManagerActivity" />
<activity android:name=".LogManagerControlActivity" />
<activity android:name=".EditEventActivity" />
<activity android:name=".RemoteDbActivity" />
<activity android:name=".ReportSeizureActivity" />
<receiver
android:name=".BootBroadcastReceiver"
@@ -72,4 +80,4 @@
android:required="false" />
</application>
</manifest>
</manifest>

View File

@@ -18,8 +18,6 @@
package com.rohitss.uceh;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -28,7 +26,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
@@ -39,6 +36,9 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -54,7 +54,7 @@ import uk.org.openseizuredetector.R;
* <p>This class is used to </p>
* Created by Rohit.
*/
public final class UCEDefaultActivity extends Activity {
public final class UCEDefaultActivity extends AppCompatActivity {
private File txtFile;
private String strCurrentErrorLog;
private String TAG = "UCEDefaultActivity";

View File

@@ -25,6 +25,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
@@ -228,7 +230,7 @@ public final class UCEHandler {
return context.getSharedPreferences(SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE).getLong(SHARED_PREFERENCES_FIELD_TIMESTAMP, -1);
}
static void closeApplication(Activity activity) {
static void closeApplication(AppCompatActivity activity) {
activity.finish();
killCurrentProcess();
}

View File

@@ -1,85 +0,0 @@
package uk.org.openseizuredetector;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TimeZone;
/**
* Created by graham on 27/06/16.
*/
/* From https://github.com/kramimus/pebble-accel-analyzer */
public class AccelData {
private final String TAG = AccelData.class.getSimpleName();
final private int x;
final private int y;
final private int z;
private long timestamp = 0;
final private boolean didVibrate;
public AccelData(byte[] data) {
x = (data[0] & 0xff) | (data[1] << 8);
y = (data[2] & 0xff) | (data[3] << 8);
z = (data[4] & 0xff) | (data[5] << 8);
didVibrate = data[6] != 0;
for (int i = 0; i < 8; i++) {
timestamp |= ((long)(data[i+7] & 0xff)) << (i * 8);
}
}
public JSONObject toJson() {
JSONObject json = new JSONObject();
try {
json.put("x", x);
json.put("y", y);
json.put("z", z);
json.put("ts", timestamp);
json.put("v", didVibrate);
return json;
} catch (JSONException e) {
Log.w(TAG, "problem constructing accel data, skipping " + e);
}
return null;
}
public static List<AccelData> fromDataArray(byte[] data) {
List<AccelData> accels = new ArrayList<AccelData>();
for (int i = 0; i < data.length; i += 15) {
accels.add(new AccelData(Arrays.copyOfRange(data, i, i + 15)));
}
return accels;
}
public long getTimestamp() {
return timestamp;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
public int getMagnitude() {
return (int)Math.sqrt(x*x + y*y + z*z);
}
public void applyTimezone(TimeZone tz) {
timestamp -= tz.getOffset(timestamp);
}
}

View File

@@ -1,78 +0,0 @@
package uk.org.openseizuredetector;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
public class AuthDialog extends DialogFragment {
private String TAG = "AuthDialog";
private AuthDialogInterface mListener;
private Context mContext;
private EditText mUnameEt;
private EditText mPasswdEt;
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
Log.v(TAG, "onCreateView()");
View v = inflater.inflate(R.layout.dialog_authenticate,
container, false);
Button cancelBtn =
(Button) v.findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel);
Button OKBtn = (Button) v.findViewById(R.id.OKBtn);
OKBtn.setOnClickListener(onOK);
mUnameEt = (EditText) v.findViewById(R.id.username);
mPasswdEt = (EditText) v.findViewById(R.id.password);
return v;
}
View.OnClickListener onCancel =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onCancel");
//m_status=false;
mListener.onDialogDone(false);
dismiss();
}
};
View.OnClickListener onOK =
new View.OnClickListener() {
@Override
public void onClick(View view) {
//m_status=true;
Log.v(TAG, "onOK()");
String uname = mUnameEt.getText().toString();
String passwd = mPasswdEt.getText().toString();
Log.v(TAG,"onOK() - uname="+uname+", passwd="+passwd);
mListener.onDialogDone(true);
dismiss();
}
};
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (AuthDialogInterface) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement dialogDoneistener");
}
}
}

View File

@@ -1,5 +0,0 @@
package uk.org.openseizuredetector;
public interface AuthDialogInterface {
void onDialogDone(boolean state);
}

View File

@@ -0,0 +1,343 @@
package uk.org.openseizuredetector;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.firebase.ui.auth.AuthUI;
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;
public class AuthenticateActivity extends AppCompatActivity {
private String TAG = "AuthenticateActivity";
private OsdUtil mUtil;
private EditText mUnameEt;
private EditText mPasswdEt;
private SdServiceConnection mConnection;
final Handler serverStatusHandler = new Handler();
private WebApiConnection mWac;
private LogManager mLm;
private static final String TOKEN_ID = "webApiAuthToken";
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authenticate);
mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler);
if (!mUtil.isServerRunning()) {
mUtil.showToast(getString(R.string.error_server_not_running));
finish();
return;
}
Button cancelBtn =
(Button) findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel);
Button loginBtn = (Button) findViewById(R.id.loginBtn);
loginBtn.setOnClickListener(onLogin);
Button logoutCancelBtn =
(Button) findViewById(R.id.logoutCancelBtn);
logoutCancelBtn.setOnClickListener(onCancel);
Button logoutBtn = (Button) findViewById(R.id.logoutBtn);
logoutBtn.setOnClickListener(onLogout);
// Components required only for osdapi backend
if (LogManager.USE_FIREBASE_BACKEND) { }
else {
mConnection = new SdServiceConnection(getApplicationContext());
Button registerBtn = (Button) findViewById(R.id.RegisterBtn);
registerBtn.setOnClickListener(onRegister);
Button resetPasswordBtn = (Button) findViewById(R.id.ResetPasswordBtn);
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
protected void onStart() {
Log.d(TAG, "onStart()");
super.onStart();
if (LogManager.USE_FIREBASE_BACKEND) {
updateUi();
} else {
mUtil.bindToServer(getApplicationContext(), mConnection);
waitForConnection();
}
}
@Override
protected void onStop() {
Log.d(TAG, "onStop()");
super.onStop();
if (LogManager.USE_FIREBASE_BACKEND) {
} else {
mUtil.unbindFromServer(getApplicationContext(), mConnection);
}
}
private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for
// the mConnection to bind to the service, so we delay half a second to give it chance
// to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer)
if (mConnection.mBound) {
Log.v(TAG, "waitForConnection - Bound!");
initialiseServiceConnection();
} else {
Log.v(TAG, "waitForConnection - waiting...");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
waitForConnection();
}
}, 100);
}
}
private void initialiseServiceConnection() {
Log.v(TAG,"initialiseServiceConnection()");
mLm = mConnection.mSdServer.mLm;
mWac = mConnection.mSdServer.mLm.mWac;
updateUi();
}
// Called after the Firebase Auth UI has completed
private ActivityResultLauncher<Intent> signInLauncher = registerForActivityResult(
new FirebaseAuthUIActivityResultContract(),
(result) -> {
Log.i(TAG, "FirebaseAuthUIActivityResult - " + result.toString());
updateUi();
});
// ...
private void updateUi() {
Log.v(TAG,"updateUi()");
LinearLayout loginLl = (LinearLayout) findViewById(R.id.login_ui);
LinearLayout osdApiLoginLl = (LinearLayout) findViewById(R.id.login_osdapi_ui);
LinearLayout logoutLl = (LinearLayout) findViewById(R.id.logout_ui);
if (mWac == null) {
Log.i(TAG,"mWac is null - not updating UI");
return;
}
if (mWac.isLoggedIn()) {
Log.v(TAG, "Already Logged in - showing Log Out prompt");
loginLl.setVisibility(View.GONE);
logoutLl.setVisibility(View.VISIBLE);
if (!LogManager.USE_FIREBASE_BACKEND) {
osdApiLoginLl.setVisibility(View.GONE);
}
mWac.getUserProfile((JSONObject profileObj) -> {
try {
String userId = profileObj.getString("id");
String userName = profileObj.getString("username");
TextView tv2 = (TextView) findViewById(R.id.userIdTv);
tv2.setText(userId);
tv2 = (TextView) findViewById(R.id.usernameTv);
tv2.setText(userName);
} catch (JSONException e) {
Log.e(TAG, "Error Parsing profileObj: " + e.getMessage());
mUtil.showToast("Error Parsing profileObj - this should not happen!!!");
}
});
} else {
Log.v(TAG,"updateUi() - not logged in..");
loginLl.setVisibility(View.VISIBLE);
logoutLl.setVisibility(View.GONE);
if (!LogManager.USE_FIREBASE_BACKEND) {
osdApiLoginLl.setVisibility(View.VISIBLE);
}
}
}
View.OnClickListener onCancel =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onCancel");
//m_status=false;
finish();
}
};
View.OnClickListener onLogin =
new View.OnClickListener() {
@Override
public void onClick(View view) {
//m_status=true;
if (LogManager.USE_FIREBASE_BACKEND) {
Log.v(TAG, "onLogin() - using Firebase Login");
Intent signInIntent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(Arrays.asList(
new AuthUI.IdpConfig.GoogleBuilder().build(),
//new AuthUI.IdpConfig.FacebookBuilder().build(),
//new AuthUI.IdpConfig.TwitterBuilder().build(),
//new AuthUI.IdpConfig.MicrosoftBuilder().build(),
//new AuthUI.IdpConfig.YahooBuilder().build(),
//new AuthUI.IdpConfig.AppleBuilder().build(),
new AuthUI.IdpConfig.EmailBuilder().build()
//new AuthUI.IdpConfig.PhoneBuilder().build()
//new AuthUI.IdpConfig.AnonymousBuilder().build()))
))
// ... options ...
.build();
signInLauncher.launch(signInIntent);
} else {
// Use Username and password authentication for OSDAPI.
// FIXME - make this work with Google Authentication like we do for Firebase.
String uname = mUnameEt.getText().toString();
String passwd = mPasswdEt.getText().toString();
Log.v(TAG,"onOK() - uname="+uname+", passwd="+passwd);
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);
}
}
});
}
}
};
View.OnClickListener onLogout = new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onLogout");
if (LogManager.USE_FIREBASE_BACKEND) {
AuthUI.getInstance()
.signOut(getApplicationContext())
.addOnCompleteListener(new OnCompleteListener<Void>() {
public void onComplete(@NonNull Task<Void> task) {
// user is now signed out
updateUi();
}
});
} else {
if (mWac != null) {
mWac.logout();
saveAuthToken(null);
} else {
Log.e(TAG,"logout() - mWac is null - not doing anything");
}
}
updateUi();
}
};
View.OnClickListener onRegister =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "onRegisterBtn");
//Intent i;
//i = new Intent(getApplicationContext(), RemoteDbActivity.class);
//i.putExtra("url", "https://osdapi.ddns.net/static/register.html");
//startActivity(i);
String url = "https://osdapi.ddns.net/static/register.html";
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}
};
View.OnClickListener onResetPassword =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "onResetPasswordBtn");
//Intent i;
//i = new Intent(getApplicationContext(), RemoteDbActivity.class);
//i.putExtra("url", "https://osdapi.ddns.net/static/register.html");
//startActivity(i);
String url = "https://osdapi.ddns.net/static/request_password_reset.html";
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}
};
private void saveAuthToken(String tokenStr) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
prefs.edit().putString(TOKEN_ID, tokenStr).commit();
mWac.setStoredToken(tokenStr);
}
public String getAuthToken() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String authToken = prefs.getString(TOKEN_ID, null);
return authToken;
}
}

View File

@@ -17,12 +17,17 @@ package uk.org.openseizuredetector;
*/
import static androidx.core.content.PermissionChecker.PERMISSION_GRANTED;
import android.Manifest;
import android.app.Activity;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -30,19 +35,23 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.MenuItemCompat;
import java.util.ArrayList;
/**
@@ -51,16 +60,20 @@ import java.util.ArrayList;
public class BLEScanActivity extends ListActivity {
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mBluetoothLeScanner;
private boolean mScanning;
private Handler mHandler;
private boolean bleAvailable = false;
private boolean mPermissionsRequested = false;
private final String TAG = "BLEScanActivity";
private final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
//Manifest.permission.BLUETOOTH_PRIVILEGED,
};
private static final int REQUEST_ENABLE_BT = 1;
@@ -70,6 +83,7 @@ public class BLEScanActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ble_scan_activity);
//this.getActionBar().setTitle(R.string.title_devices);
this.setTitle(R.string.title_devices);
mHandler = new Handler();
@@ -79,6 +93,8 @@ public class BLEScanActivity extends ListActivity {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
} else {
bleAvailable = true;
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
@@ -93,6 +109,8 @@ public class BLEScanActivity extends ListActivity {
finish();
return;
}
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
}
@Override
@@ -101,7 +119,7 @@ public class BLEScanActivity extends ListActivity {
if (!mScanning) {
menu.findItem(R.id.menu_stop).setVisible(false);
menu.findItem(R.id.menu_scan).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(null);
MenuItemCompat.setActionView(menu.findItem(R.id.menu_refresh), null);
} else {
menu.findItem(R.id.menu_stop).setVisible(true);
menu.findItem(R.id.menu_scan).setVisible(false);
@@ -125,17 +143,79 @@ public class BLEScanActivity extends ListActivity {
return true;
}
public void onScanButtonClick(View v) {
scanLeDevice(true);
}
@Override
protected void onResume() {
super.onResume();
SharedPreferences SP = PreferenceManager
.getDefaultSharedPreferences(this);
TextView tv = (TextView) findViewById(R.id.current_ble_device_tv);
try {
String bleAddr = SP.getString("BLE_Device_Addr", "none");
String bleName = SP.getString("BLE_Device_Name", "none");
tv.setText("Current Device=" + bleName + " (" + bleAddr + ")");
} catch (Exception e) {
tv.setText("Current Device=" + "none" + " (" + "none" + ")");
}
tv = (TextView) findViewById(R.id.ble_present_tv);
if (mBluetoothAdapter == null) {
tv.setText("ERROR - Bluetooth Adapter Not Present");
} else {
tv.setText("Bluetooth Adapter Present - OK");
}
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
tv = (TextView) findViewById(R.id.ble_adapter_tv);
if (!mBluetoothAdapter.isEnabled()) {
tv.setText("ERROR - Bluetoot NOT Enabled");
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
tv.setText("Bluetooth Adapter Enabled OK");
}
requestBTPermissions(this);
for (int i = 0; i < REQUIRED_PERMISSIONS.length; i++) {
if (ContextCompat.checkSelfPermission(this, REQUIRED_PERMISSIONS[i]) == PERMISSION_GRANTED) {
Log.i(TAG, "Permission " + REQUIRED_PERMISSIONS[i] + " OK");
} else {
Log.e(TAG, "Permission " + REQUIRED_PERMISSIONS[i] + " NOT GRANTED");
Toast.makeText(this, "ERROR - Permission " + REQUIRED_PERMISSIONS[i] + " not Granted - this will not work!!!!!", Toast.LENGTH_SHORT).show();
}
}
tv = (TextView) findViewById(R.id.ble_perm1_tv);
if (ContextCompat.checkSelfPermission(this, REQUIRED_PERMISSIONS[0]) == PERMISSION_GRANTED) {
tv.setText("Permission " + REQUIRED_PERMISSIONS[0] + " OK");
} else {
tv.setText("ERROR: Permission " + REQUIRED_PERMISSIONS[0] + " NOT GRANTED");
}
tv = (TextView) findViewById(R.id.ble_perm2_tv);
if (ContextCompat.checkSelfPermission(this, REQUIRED_PERMISSIONS[1]) == PERMISSION_GRANTED) {
tv.setText("Permission " + REQUIRED_PERMISSIONS[1] + " OK");
} else {
tv.setText("ERROR: Permission " + REQUIRED_PERMISSIONS[1] + " NOT GRANTED");
}
tv = (TextView) findViewById(R.id.ble_perm3_tv);
if (ContextCompat.checkSelfPermission(this, REQUIRED_PERMISSIONS[2]) == PERMISSION_GRANTED) {
tv.setText("Permission " + REQUIRED_PERMISSIONS[2] + " OK");
} else {
tv.setText("ERROR: Permission " + REQUIRED_PERMISSIONS[2] + " NOT GRANTED");
}
tv = (TextView) findViewById(R.id.ble_perm4_tv);
if (ContextCompat.checkSelfPermission(this, REQUIRED_PERMISSIONS[3]) == PERMISSION_GRANTED) {
tv.setText("Permission " + REQUIRED_PERMISSIONS[3] + " OK");
} else {
tv.setText("ERROR: Permission " + REQUIRED_PERMISSIONS[3] + " NOT GRANTED");
}
// Initializes list view adapter.
mLeDeviceListAdapter = new LeDeviceListAdapter();
setListAdapter(mLeDeviceListAdapter);
@@ -166,10 +246,10 @@ public class BLEScanActivity extends ListActivity {
if (device == null) return;
Log.v(TAG, "onListItemClick: Device=" + device.getName() + ", Addr=" + device.getAddress());
if (mScanning) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mBluetoothLeScanner.stopScan(mLeScanCallback);
mScanning = false;
}
Log.v(TAG,"Saving Device Details");
Log.v(TAG, "Saving Device Details");
SharedPreferences.Editor SPE = PreferenceManager
.getDefaultSharedPreferences(this).edit();
try {
@@ -178,36 +258,70 @@ public class BLEScanActivity extends ListActivity {
SPE.apply();
SPE.commit();
Log.v(TAG, "Saved Device Name="+device.getName()+" and Address="+device.getAddress());
Log.v(TAG, "Saved Device Name=" + device.getName() + " and Address=" + device.getAddress());
} catch (Exception ex) {
Log.e(TAG, "Error Saving Devie Name and Address!");
Log.e(TAG, "Error Saving Device Name and Address!");
Toast toast = Toast.makeText(this, "Problem Saving Device Name and Address", Toast.LENGTH_SHORT);
toast.show();
}
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences((this));
Log.v(TAG,"Check of saved values - Name="+SP.getString("BLE_Device_Name","NOT SET")+", Addr="+SP.getString("BLE_Device_Addr","NOT SET"));
Log.v(TAG, "Check of saved values - Name=" + SP.getString("BLE_Device_Name", "NOT SET") + ", Addr=" + SP.getString("BLE_Device_Addr", "NOT SET"));
finish();
}
public void requestBTPermissions(Activity activity) {
if (mPermissionsRequested) {
Log.i(TAG, "requestPermissions() - request already sent - not doing anything");
} else {
Log.i(TAG, "requestPermissions() - requesting permissions");
for (int i = 0; i < REQUIRED_PERMISSIONS.length; i++) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
REQUIRED_PERMISSIONS[i])) {
Log.i(TAG, "shouldShowRationale for permission" + REQUIRED_PERMISSIONS[i]);
}
}
ActivityCompat.requestPermissions(activity,
REQUIRED_PERMISSIONS,
42);
mPermissionsRequested = true;
}
}
private void scanLeDevice(final boolean enable) {
requestPermissions(this);
TextView tv;
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mBluetoothLeScanner.stopScan(mLeScanCallback);
invalidateOptionsMenu();
TextView tv = (TextView) (findViewById(R.id.ble_scan_status_tv));
tv.setText("Stopped");
Button b = (Button) findViewById(R.id.startScanButton);
b.setEnabled(true);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
mBluetoothLeScanner.startScan(mLeScanCallback);
tv = (TextView) (findViewById(R.id.ble_scan_status_tv));
tv.setText("Scanning");
Button b = (Button) findViewById(R.id.startScanButton);
b.setEnabled(false);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mBluetoothLeScanner.stopScan(mLeScanCallback);
tv = (TextView) (findViewById(R.id.ble_scan_status_tv));
tv.setText("Stopped");
Button b = (Button) findViewById(R.id.startScanButton);
b.setEnabled(true);
}
invalidateOptionsMenu();
}
@@ -225,7 +339,7 @@ public class BLEScanActivity extends ListActivity {
public void addDevice(BluetoothDevice device) {
if (!mLeDevices.contains(device)) {
Log.v(TAG,"addDevice - "+device.getName());
Log.v(TAG, "addDevice - " + device.getName());
mLeDevices.add(device);
}
}
@@ -256,7 +370,7 @@ public class BLEScanActivity extends ListActivity {
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
Log.v(TAG,"scanner getView i="+i);
Log.v(TAG, "scanner getView i=" + i);
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.ble_list_item_device, null);
@@ -281,19 +395,14 @@ public class BLEScanActivity extends ListActivity {
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
private ScanCallback mLeScanCallback =
new ScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
Log.v(TAG,"LEScanCallback - device="+device.getName());
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
public void onScanResult(int callbackType, ScanResult result) {
//super.onScanResult(callbackType, result);
Log.v(TAG, "ScanCallback - " + result.getDevice().getName());
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
};
@@ -303,21 +412,4 @@ public class BLEScanActivity extends ListActivity {
}
public void requestPermissions(Activity activity) {
if (mPermissionsRequested) {
Log.i(TAG, "requestPermissions() - request already sent - not doing anything");
} else {
Log.i(TAG, "requestPermissions() - requesting permissions");
for (int i = 0; i < REQUIRED_PERMISSIONS.length; i++) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
REQUIRED_PERMISSIONS[i])) {
Log.i(TAG, "shouldShowRationale for permission" + REQUIRED_PERMISSIONS[i]);
}
}
ActivityCompat.requestPermissions(activity,
REQUIRED_PERMISSIONS,
42);
mPermissionsRequested = true;
}
}
}

View File

@@ -1,107 +0,0 @@
package uk.org.openseizuredetector;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
/**
* Created by graham on 28/06/16.
*/
public class CircularArrayList<E>
extends AbstractList<E> implements RandomAccess {
/**
* If you use this code, please consider notifying isak at du-preez dot com
* with a brief description of your application.
*
* This is free and unencumbered software released into the public domain.
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*/
private final int n; // buffer length
private final List<E> buf; // a List implementing RandomAccess
private int head = 0;
private int tail = 0;
public CircularArrayList(int capacity) {
n = capacity + 1;
buf = new ArrayList<E>(Collections.nCopies(n, (E) null));
}
public int capacity() {
return n - 1;
}
private int wrapIndex(int i) {
int m = i % n;
if (m < 0) { // java modulus can be negative
m += n;
}
return m;
}
// This method is O(n) but will never be called if the
// CircularArrayList is used in its typical/intended role.
private void shiftBlock(int startIndex, int endIndex) {
assert (endIndex > startIndex);
for (int i = endIndex - 1; i >= startIndex; i--) {
set(i + 1, get(i));
}
}
@Override
public int size() {
return tail - head + (tail < head ? n : 0);
}
@Override
public E get(int i) {
if (i < 0 || i >= size()) {
throw new IndexOutOfBoundsException();
}
return buf.get(wrapIndex(head + i));
}
@Override
public E set(int i, E e) {
if (i < 0 || i >= size()) {
throw new IndexOutOfBoundsException();
}
return buf.set(wrapIndex(head + i), e);
}
@Override
public void add(int i, E e) {
int s = size();
if (s == n - 1) {
throw new IllegalStateException("Cannot add element."
+ " CircularArrayList is filled to capacity.");
}
if (i < 0 || i > s) {
throw new IndexOutOfBoundsException();
}
tail = wrapIndex(tail + 1);
if (i < s) {
shiftBlock(i, s);
}
set(i, e);
}
@Override
public E remove(int i) {
int s = size();
if (i < 0 || i >= s) {
throw new IndexOutOfBoundsException();
}
E e = get(i);
if (i > 0) {
shiftBlock(0, i);
}
head = wrapIndex(head + 1);
return e;
}
}

View File

@@ -0,0 +1,360 @@
package uk.org.openseizuredetector;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class EditEventActivity extends AppCompatActivity {
private String TAG = "EditEventActivity";
private Context mContext;
private WebApiConnection mWac;
private LogManager mLm;
private SdServiceConnection mConnection;
final Handler serverStatusHandler = new Handler();
private OsdUtil mUtil;
private List<String> mEventTypesList = null;
private HashMap<String, ArrayList<String>> mEventSubTypesHashMap = null;
private String mEventTypeStr = null;
private String mEventSubTypeStr = null;
private String mEventId;
private String mEventNotes = "";
//private Date mEventDateTime;
private RadioGroup mEventTypeRg;
private boolean mEventTypesListChanged = false;
private RadioGroup mEventSubTypeRg;
private boolean mEventSubTypesListChanged = false;
private JSONObject mEventObj;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_event);
mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler);
mConnection = new SdServiceConnection(getApplicationContext());
//mWac = new WebApiConnection(this, this, this, this);
//mLm = new LogManager(this);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String eventId = extras.getString("eventId");
mEventId = eventId;
Log.v(TAG, "onCreate - mEventId=" + mEventId);
}
Button cancelBtn =
(Button) findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel);
Button OKBtn = (Button) findViewById(R.id.loginBtn);
OKBtn.setOnClickListener(onOK);
mEventTypeRg = findViewById(R.id.eventTypeRg);
mEventTypeRg.setOnCheckedChangeListener(onEventTypeChange);
mEventSubTypeRg = findViewById(R.id.eventSubTypeRg);
mEventSubTypeRg.setOnCheckedChangeListener(onEventSubTypeChange);
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart()");
mUtil.bindToServer(getApplicationContext(), mConnection);
waitForConnection();
updateUi();
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop()");
mUtil.unbindFromServer(getApplicationContext(), mConnection);
}
private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for
// the mConnection to bind to the service, so we delay half a second to give it chance
// to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer)
if (mConnection.mBound) {
Log.v(TAG, "waitForConnection - Bound!");
initialiseServiceConnection();
} else {
Log.v(TAG, "waitForConnection - waiting...");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
waitForConnection();
}
}, 100);
}
}
private void initialiseServiceConnection() {
mLm = mConnection.mSdServer.mLm;
mWac = mConnection.mSdServer.mLm.mWac;
// Retrieve the JSONObject containing the standard event types.
// Note this obscure syntax is to avoid having to create another interface, so it is worth it :)
// See https://medium.com/@pra4mesh/callback-function-in-java-20fa48b27797
mWac.getEventTypes(new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventTypesObj) {
Log.v(TAG, "initialiseServiceConnection().onEventTypesReceived");
if (eventTypesObj == null) {
Log.e(TAG, "initialiseServiceConnection().getEventTypes Callback: Error Retrieving event types");
mUtil.showToast("Error Retrieving Event Types from Server - Please Try Again Later!");
} else {
Iterator<String> keys = eventTypesObj.keys();
mEventTypesList = new ArrayList<String>();
mEventSubTypesHashMap = new HashMap<String, ArrayList<String>>();
while (keys.hasNext()) {
String key = keys.next();
Log.v(TAG, "initialiseServiceConnection().getEventTypes Callback: key=" + key);
mEventTypesList.add(key);
try {
JSONArray eventSubTypes = eventTypesObj.getJSONArray(key);
ArrayList<String> eventSubtypesList = new ArrayList<String>();
for (int i = 0; i < eventSubTypes.length(); i++) {
eventSubtypesList.add(eventSubTypes.getString(i));
}
mEventSubTypesHashMap.put(key, eventSubtypesList);
mEventTypesListChanged = true;
} catch (JSONException e) {
Log.e(TAG, "initialiseServiceConnection().getEventTypes Callback: Error parsing JSONObject" + e.getMessage() + e.toString());
}
}
updateUi();
}
}
});
// Retrieve the event data to edit
try {
mWac.getEvent(mEventId, new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventObj) {
Log.v(TAG, "initialiseServiceConnection.getEvent");
if (eventObj != null) {
mEventObj = eventObj;
Log.v(TAG, "initialiseServiceConnection.getEvent: eventObj=" + eventObj.toString());
updateUi();
// FIXME: modify updateUi to use mEventObj
} else {
mUtil.showToast("Failed to Retrieve Event from Remote Database");
finish();
}
}
});
} catch (Exception e) {
Log.e(TAG, "ERROR:" + e.getMessage());
e.printStackTrace();
}
}
private void updateUi() {
Log.v(TAG, "updateUI");
TextView tv;
RadioButton b;
// Populate event type button group if necessary
if (mEventTypesList != null && mEventTypesListChanged) {
Log.v(TAG, "updateUi: " + mEventTypesList.toString());
mEventTypeRg.removeAllViews();
for (String eventTypeStr : mEventTypesList) {
b = new RadioButton(this);
b.setText(eventTypeStr);
mEventTypeRg.addView(b);
}
mEventTypesListChanged = false;
}
try {
if (mEventObj != null) {
tv = (TextView) findViewById(R.id.eventIdTv);
tv.setText(mEventId);
tv = (TextView) findViewById(R.id.eventAlarmStateTv);
String alarmStateStr = mEventObj.getString("osdAlarmState");
try {
int alarmStateVal = Integer.parseInt(alarmStateStr);
alarmStateStr = mUtil.alarmStatusToString(alarmStateVal);
} catch (Exception e) {
Log.v(TAG,"updateUi: alarmState does not parse to int so displaying it as string: " +alarmStateStr);
}
tv.setText(alarmStateStr);
tv = (TextView) findViewById(R.id.eventNotsTv);
tv.setText(mEventObj.getString("desc"));
tv = (TextView) findViewById(R.id.eventDateTv);
try {
String dateStr = mEventObj.getString("dataTime");
Date dataTime = mUtil.string2date(dateStr);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
tv.setText(dateFormat.format(dataTime));
} catch (Exception e) {
Log.e(TAG,"updateUI: Error Parsing dataDate "+e.getLocalizedMessage());
tv.setText("---");
}
// Check the correct seizure type button in the event type group
for (int index = 0; index < mEventTypeRg.getChildCount(); index++) {
b = (RadioButton) mEventTypeRg.getChildAt(index);
String buttonText = b.getText().toString();
if (buttonText.equals(mEventObj.getString("type"))) {
Log.v(TAG, "updateUi - selecting button " + mEventObj.getString("type"));
b.setChecked(true);
}
}
// Populate the event sub-types radio button list.
Log.v(TAG,"updateUi() - meventsubtypeshashmap="+mEventSubTypesHashMap+", mEventSubtypesListChanged="+mEventSubTypesListChanged);
if (mEventSubTypesHashMap != null && mEventSubTypesListChanged) {
Log.v(TAG,"UpdateUi() - populating event sub types list");
if (mEventObj.getString("type") != null) {
// based on https://androidexample.com/create-a-simple-listview
ArrayList<String> subtypesArrayList = mEventSubTypesHashMap.get(mEventObj.getString("type"));
Log.v(TAG, "updateUi() - eventType=" + mEventObj.getString("type") + ", subtypes=" + subtypesArrayList);
mEventSubTypeRg.removeAllViews();
for (String eventSubTypeStr : subtypesArrayList) {
b = new RadioButton(this);
b.setText(eventSubTypeStr);
mEventSubTypeRg.addView(b);
}
mEventSubTypesListChanged = false;
}
}
// And show the correct sub-type selected.
for (int index = 0; index < mEventSubTypeRg.getChildCount(); index++) {
b = (RadioButton) mEventSubTypeRg.getChildAt(index);
String buttonText = b.getText().toString();
if (buttonText.equals(mEventObj.getString("subType"))) {
Log.v(TAG, "updateUi - selecting button " + mEventObj.getString("subType"));
b.setChecked(true);
}
}
}
} catch (JSONException e) {
Log.e(TAG,"Error Parsing mEventObj: "+e.getMessage());
}
} // updateUi()
View.OnClickListener onCancel =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onCancel");
//m_status=false;
finish();
}
};
View.OnClickListener onOK =
new View.OnClickListener() {
@Override
public void onClick(View view) {
//m_status=true;
TextView tv = (TextView)findViewById(R.id.eventNotsTv);
try {
mEventObj.put("desc",tv.getText());
mEventObj.put("id",mEventId); // Add event Id to event object manually because firestore does not include it by default.
} catch (JSONException e) {
Log.e(TAG,"Error writing mEventObj: "+e.getMessage());
}
Log.v(TAG, "onOK() - eventObj="+mEventObj.toString());
try {
mWac.updateEvent(mEventObj, new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventObj) {
Log.v(TAG, "onOk.updateEvent");
//mEventObj = eventObj;
if (eventObj != null) {
Log.v(TAG, "onOk.getEvent: eventObj=" + eventObj.toString());
mUtil.showToast("Event Updated OK");
finish();
} else {
Log.e(TAG, "onOk.updateEvent - Error - returned NULL");
mUtil.showToast("Error Updating Event");
updateUi();
}
}
});
} catch (Exception e) {
Log.e(TAG,"onOK() - ERROR: "+e.getMessage()+" : " +e.toString());
e.printStackTrace();
mUtil.showToast("Error Updating Event");
updateUi();
}
}
};
RadioGroup.OnCheckedChangeListener onEventTypeChange =
new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
Log.v(TAG,"onEventTypeChange() - id="+checkedId);
RadioButton b = (RadioButton)findViewById(group.getCheckedRadioButtonId());
String selectedEventType = b.getText().toString();
try {
mEventObj.put("type", selectedEventType);
} catch (JSONException e) {
Log.e(TAG,"Error setting mEventObj.type: "+e.getMessage());
}
mEventSubTypesListChanged = true;
Log.v(TAG,"onEventTypeChange() - mEventSubTypesListChanged="+mEventSubTypesListChanged);
updateUi();
}
};
RadioGroup.OnCheckedChangeListener onEventSubTypeChange =
new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
Log.v(TAG,"onEventSubTypeChange() - id="+checkedId);
RadioButton b = (RadioButton)findViewById(group.getCheckedRadioButtonId());
String selectedEventSubType = b.getText().toString();
try {
mEventObj.put("subType", selectedEventSubType);
} catch (JSONException e) {
Log.e(TAG,"Error setting mEventObj.type: "+e.getMessage());
}
updateUi();
}
};
}

View File

@@ -1,98 +0,0 @@
package uk.org.openseizuredetector.EventLogManager;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import uk.org.openseizuredetector.R;
public class EventLogListAdapter extends BaseAdapter {
EventLogManager dm;
ArrayList<LogEntryModel> logEntryModelList;
LayoutInflater inflater;
Context _context;
public EventLogListAdapter(Context context) {
logEntryModelList = new ArrayList<LogEntryModel>();
_context = context;
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
dm = new EventLogManager(_context);
logEntryModelList = dm.getAllData();
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
//refetching the new data from database
logEntryModelList = dm.getAllData();
}
public void delRow(int delPosition) {
dm.deleteRow(logEntryModelList.get(delPosition).getId());
logEntryModelList.remove(delPosition);
}
@Override
public int getCount() {
return logEntryModelList.size();
}
@Override
public Object getItem(int position) {
return logEntryModelList.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vHolder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.log_entry_layout, null);
vHolder = new ViewHolder();
vHolder.date = (TextView) convertView
.findViewById(R.id.event_date);
vHolder.alarmState = (TextView) convertView
.findViewById(R.id.event_alarmState);
vHolder.note = (TextView) convertView
.findViewById(R.id.event_note);
vHolder.dataJSON = (TextView) convertView
.findViewById(R.id.event_dataJSON);
convertView.setTag(vHolder);
} else {
vHolder = (ViewHolder) convertView.getTag();
}
LogEntryModel eventObj = logEntryModelList.get(position);
//vHolder.date.setText(eventObj.getDate().toString());
vHolder.alarmState.setText(eventObj.getAlarmState());
vHolder.note.setText(eventObj.getNote());
vHolder.dataJSON.setText(eventObj.getDataJSON());
return convertView;
}
class ViewHolder {
TextView date,alarmState,note,dataJSON;
}
}

View File

@@ -1,210 +0,0 @@
/**
* Database manager for logging events and associated seizure detector data.
*/
package uk.org.openseizuredetector.EventLogManager;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class EventLogManager {
final static String TAG = "EventLogManager";
private SQLiteDatabase db; // a reference to the database manager class.
private static final String DB_NAME = "eventlog"; // the name of our database
private static final int DB_VERSION = 1; // the version of the database
private static final String TABLE_NAME = "events";// table name
// the names for our database columns
private static final String TABLE_ROW_ID = "_id";
private static final String TABLE_ROW_DATE = "event_date";
private static final String TABLE_ROW_ALARM_STATE = "alarm_state";
private static final String TABLE_ROW_DATA_JSON = "data_json";
private static final String TABLE_ROW_NOTE = "note";
private Context context;
public EventLogManager(Context context) {
this.context = context;
// create or open the database
CustomSQLiteOpenHelper helper = new CustomSQLiteOpenHelper(context);
this.db = helper.getWritableDatabase();
helper.onCreate(this.db);
}
// the beginnings our SQLiteOpenHelper class
private class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
public CustomSQLiteOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// the SQLite query string that will create our column database
// table.
String newTableQueryString = "create table " + TABLE_NAME + " ("
+ TABLE_ROW_ID
+ " integer primary key autoincrement not null,"
+ TABLE_ROW_DATE + " timestamp not null," + TABLE_ROW_ALARM_STATE
+ " integer not null," + TABLE_ROW_NOTE + " text not null,"
+ TABLE_ROW_DATA_JSON + " text not null" + ");";
// execute the query string to the database.
db.execSQL(newTableQueryString);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// LATER, WE WOULD SPECIFIY HOW TO UPGRADE THE DATABASE
// FROM OLDER VERSIONS.
String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME;
db.execSQL(DROP_TABLE);
onCreate(db);
}
}
public void addRow(LogEntryModel eventObj) {
ContentValues values = prepareData(eventObj);
// ask the database object to insert the new data
try {
db.insert(TABLE_NAME, null, values);
} catch (Exception e) {
Log.e("DB ERROR", e.toString()); // prints the error message to
// the log
e.printStackTrace(); // prints the stack trace to the log
}
}
private String getDateTime(Date date) {
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault());
if (date==null)
return "";
else
return dateFormat.format(date);
}
private ContentValues prepareData(LogEntryModel eventObj) {
ContentValues values = new ContentValues();
values.put(TABLE_ROW_ALARM_STATE, eventObj.getAlarmState());
values.put(TABLE_ROW_DATE, getDateTime(eventObj.getDate()));
values.put(TABLE_ROW_NOTE, eventObj.getNote());
values.put(TABLE_ROW_DATA_JSON, eventObj.getDataJSON());
return values;
}
// Returns row data in form of LogEntryModel object
public LogEntryModel getRowAsObject(int rowID) {
LogEntryModel rowContactObj = new LogEntryModel();
Cursor cursor;
try {
cursor = db.query(TABLE_NAME, new String[] { TABLE_ROW_ID,
TABLE_ROW_ALARM_STATE, TABLE_ROW_DATE, TABLE_ROW_NOTE,
TABLE_ROW_DATA_JSON }, TABLE_ROW_ID + "=" + rowID, null,
null, null, null, null);
cursor.moveToFirst();
prepareSendObject(rowContactObj, cursor);
} catch (SQLException e) {
Log.e("DB ERROR", e.toString());
e.printStackTrace();
}
return rowContactObj;
}
// Returns all the rows data in form of LogEntryModel object list
public ArrayList<LogEntryModel> getAllData() {
ArrayList<LogEntryModel> allRowsObj = new ArrayList<LogEntryModel>();
Cursor cursor;
LogEntryModel rowContactObj;
String[] columns = new String[] { TABLE_ROW_ID, TABLE_ROW_ALARM_STATE,
TABLE_ROW_DATE, TABLE_ROW_NOTE, TABLE_ROW_DATA_JSON };
try {
cursor = db
.query(TABLE_NAME, columns, null, null, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
do {
rowContactObj = new LogEntryModel();
rowContactObj.setId(cursor.getInt(0));
prepareSendObject(rowContactObj, cursor);
allRowsObj.add(rowContactObj);
} while (cursor.moveToNext()); // try to move the cursor's
// pointer forward one position.
}
} catch (SQLException e) {
Log.e("DB ERROR", e.toString());
e.printStackTrace();
}
return allRowsObj;
}
private void prepareSendObject(LogEntryModel rowObj, Cursor cursor) {
rowObj.setId(cursor.getInt(cursor.getColumnIndexOrThrow(TABLE_ROW_ID)));
rowObj.setAlarmState(cursor.getInt(cursor
.getColumnIndexOrThrow(TABLE_ROW_ALARM_STATE)));
String dateTimeStr = cursor.getString(cursor
.getColumnIndexOrThrow(TABLE_ROW_DATE));
Log.v(TAG,"dateTimeStr = "+dateTimeStr);
Date dateVal;
try { dateVal = new Date(dateTimeStr); }
catch (IllegalArgumentException e) { dateVal = null; }
rowObj.setDate(dateVal);
rowObj.setNote(cursor.getString(cursor
.getColumnIndexOrThrow(TABLE_ROW_NOTE)));
rowObj.setDataJSON(cursor.getString(cursor
.getColumnIndexOrThrow(TABLE_ROW_DATA_JSON)));
}
public void deleteRow(int rowID) {
// ask the database manager to delete the row of given id
try {
db.delete(TABLE_NAME, TABLE_ROW_ID + "=" + rowID, null);
} catch (Exception e) {
Log.e("DB ERROR", e.toString());
e.printStackTrace();
}
}
public void updateRow(int rowId, LogEntryModel contactObj) {
ContentValues values = prepareData(contactObj);
String whereClause = TABLE_ROW_ID + "=?";
String whereArgs[] = new String[] { String.valueOf(rowId) };
db.update(TABLE_NAME, values, whereClause, whereArgs);
}
}

View File

@@ -1,53 +0,0 @@
package uk.org.openseizuredetector.EventLogManager;
import java.util.Date;
/**
* Our LogEntryModel class which will have fields like id, name, contact number
* and email and corresponding getter and setter methods.
* **/
public class LogEntryModel {
private int id;
private Date date;
private int alarmState;
private String dataJSON;
private String note;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAlarmState() {
return alarmState;
}
public void setAlarmState(int alarmState) {
this.alarmState = alarmState;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getDataJSON() { return dataJSON; }
public void setDataJSON(String dataJSON) { this.dataJSON = dataJSON; }
}

View File

@@ -2,9 +2,8 @@ package uk.org.openseizuredetector;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -15,9 +14,9 @@ import android.widget.TimePicker;
import java.util.Calendar;
public class DBQueryActivity extends AppCompatActivity
public class ExportDataActivity extends AppCompatActivity
implements View.OnClickListener {
String TAG = "DBQueryActivity";
String TAG = "ExportDataActivity";
Button mDateBtn;
Button mTimeBtn;
Button mExportBtn;

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +0,0 @@
package uk.org.openseizuredetector;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import uk.org.openseizuredetector.EventLogManager.EventLogListAdapter;
import uk.org.openseizuredetector.EventLogManager.EventLogManager;
import uk.org.openseizuredetector.EventLogManager.LogEntryModel;
public class LogManagerActivity extends FragmentActivity
implements AuthDialogInterface {
private String TAG = "LogManagerActivity";
private EventLogListAdapter mEventLogListAdapter;
private ListView mEventLogListView;
private EventLogManager mElm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_log_manager);
LogEntryModel lem = new LogEntryModel();
//lem.setDate(new Date());
lem.setNote("Test Entry");
lem.setDataJSON("[]");
lem.setAlarmState(1);
//mElm = new EventLogManager(this);
//mElm.addRow(lem);
//mEventLogListAdapter = new EventLogListAdapter(this);
//mEventLogListView = (ListView) findViewById(R.id.eventLogListView);
//mEventLogListView.setAdapter(mEventLogListAdapter);
Button b;
b = (Button) findViewById(R.id.authenticate_button);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "authenticate button clicked");
AuthDialog authDialog = new AuthDialog();
authDialog.show(getSupportFragmentManager(),"authDialog");
}
});
}
public void updateUi() {
Log.v(TAG, "updateUi");
}
public void onDialogDone(boolean State) {
Log.v(TAG,"onDialogDone()");
LogManager lm = new LogManager(this);
lm.authenticate("test", "testpw");
}
}

View File

@@ -0,0 +1,653 @@
package uk.org.openseizuredetector;
//import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.IBinder;
import androidx.core.view.MenuCompat;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LogManagerControlActivity extends AppCompatActivity {
private String TAG = "LogManagerControlActivity";
private LogManager mLm;
private Context mContext;
private UiTimer mUiTimer;
private ArrayList<HashMap<String, String>> mEventsList;
private ArrayList<HashMap<String, String>> mRemoteEventsList;
private ArrayList<HashMap<String, String>> mSysLogList;
private SdServiceConnection mConnection;
private OsdUtil mUtil;
final Handler serverStatusHandler = new Handler();
private Integer mUiTimerPeriodFast = 2000; // 2 seconds - we use fast updating while UI is blank and we are waiting for first data
private Integer mUiTimerPeriodSlow = 60000; // 60 seconds - once data has been received and UI populated we only update once per minute.
private boolean mUpdateSysLog = true;
//private Integer UI_MODE_LOCAL = 0;
//private Integer UI_MODE_SHARED = 1;
//private Integer mUiMode = UI_MODE_SHARED;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
mContext = this;
mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler);
if (!mUtil.isServerRunning()) {
mUtil.showToast(getString(R.string.error_server_not_running));
finish();
return;
}
mConnection = new SdServiceConnection(getApplicationContext());
setContentView(R.layout.activity_log_manager_control);
/* Force display of overflow menu - from stackoverflow
* "how to force use of..."
*/
try {
Log.v(TAG, "trying menubar fiddle...");
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKeyField =
ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
if (menuKeyField != null) {
Log.v(TAG, "menuKeyField is not null - configuring....");
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
} else {
Log.v(TAG, "menuKeyField is null - doing nothing...");
}
} catch (Exception e) {
Log.v(TAG, "menubar fiddle exception: " + e.toString());
}
Button authBtn =
(Button) findViewById(R.id.auth_button);
authBtn.setOnClickListener(onAuth);
//Button pruneBtn =
// (Button) findViewById(R.id.pruneDatabaseBtn);
//pruneBtn.setOnClickListener(onPruneBtn);
//Button reportSeizureBtn =
// (Button) findViewById(R.id.reportSeizureBtn);
//reportSeizureBtn.setOnClickListener(onReportSeizureBtn);
Button remoteDbBtn =
(Button) findViewById(R.id.refresh_button);
remoteDbBtn.setOnClickListener(onRefreshBtn);
ListView lv = (ListView) findViewById(R.id.eventLogListView);
lv.setOnItemClickListener(onEventListClick);
lv = (ListView) findViewById(R.id.remoteEventsLv);
lv.setOnItemClickListener(onRemoteEventListClick);
}
/**
* Create Action Bar
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "onCreateOptionsMenu()");
getMenuInflater().inflate(R.menu.log_manager_activity_menu, menu);
MenuCompat.setGroupDividerEnabled(menu, true);
return true;
}
@Override
protected void onStart() {
Log.v(TAG, "onStart()");
super.onStart();
mUtil.bindToServer(getApplicationContext(), mConnection);
waitForConnection();
startUiTimer(mUiTimerPeriodFast);
}
@Override
protected void onStop() {
Log.v(TAG, "onStop()");
super.onStop();
stopUiTimer();
mUtil.unbindFromServer(getApplicationContext(), mConnection);
}
@Override
protected void onPause() {
Log.v(TAG, "onPause()");
super.onPause();
//stopUiTimer();
}
@Override
protected void onResume() {
Log.v(TAG, "onResume()");
super.onResume();
//startUiTimer();
}
private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for
// the mConnection to bind to the service, so we delay half a second to give it chance
// to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer)
if (mConnection.mBound) {
Log.v(TAG, "waitForConnection - Bound!");
initialiseServiceConnection();
} else {
Log.v(TAG, "waitForConnection - waiting...");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
waitForConnection();
}
}, 100);
}
}
// FIXME - for some reason this never gets called, which is why we have the 'waitForConnection()'
// function that polls the connection until it is connected.
public void onServiceConnected(ComponentName name, IBinder service) {
Log.w(TAG, "onServiceConnected()");
initialiseServiceConnection();
}
private void initialiseServiceConnection() {
mLm = mConnection.mSdServer.mLm;
startUiTimer(mUiTimerPeriodFast);
getRemoteEvents();
// Populate events list - we only do it once when the activity is created because the query might slow down the UI.
// We could try this code in updateUI() and see though.
// Based on https://www.tutlane.com/tutorial/android/android-sqlite-listview-with-examples
mLm.getEventsList(true, (ArrayList<HashMap<String, String>> eventsList) -> {
mEventsList = eventsList;
Log.v(TAG, "initialiseServiceConnection() - set mEventsList - Updating UI");
updateUi();
});
mUtil.getSysLogList((ArrayList<HashMap<String, String>> syslogList) -> {
mSysLogList = syslogList;
Log.v(TAG, "initialiseServiceConnection() - set mSysLogList - Updating UI");
updateUi();
});
}
private void getRemoteEvents() {
// Retrieve events from remote database
mLm.mWac.getEvents((JSONObject remoteEventsObj) -> {
Log.v(TAG, "getRemoteEvents()");
if (remoteEventsObj == null) {
Log.e(TAG, "getRemoteEvents Callback: Error Retrieving events");
mUtil.showToast("Error Retrieving Remote Events from Server - Please Try Again Later!");
} else {
//Log.v(TAG, "remoteEventsObj = " + remoteEventsObj.toString());
try {
JSONArray eventsArray = remoteEventsObj.getJSONArray("events");
mRemoteEventsList = new ArrayList<HashMap<String, String>>();
// A bit of a hack to display in reverse chronological order
for (int i = eventsArray.length() - 1; i >= 0; i--) {
JSONObject eventObj = eventsArray.getJSONObject(i);
Log.v(TAG, "getRemoteEvents() - " + eventObj.toString());
String id = null;
if (!eventObj.isNull("id")) {
id = eventObj.getString("id");
}
int osdAlarmState = -1;
if (!eventObj.isNull("osdAlarmState")) {
osdAlarmState = eventObj.getInt("osdAlarmState");
}
String dataTime = "null";
if (!eventObj.isNull("dataTime")) {
dataTime = eventObj.getString("dataTime");
Log.v(TAG, "getRemoteEvents() - dataTime=" + dataTime);
}
String typeStr = "null";
if (!eventObj.isNull("type")) {
typeStr = eventObj.getString("type");
}
String subType = "null";
if (!eventObj.isNull("subType")) {
subType = eventObj.getString("subType");
}
String desc = "null";
if (!eventObj.isNull("desc")) {
desc = eventObj.getString("desc");
}
HashMap<String, String> eventHashMap = new HashMap<String, String>();
eventHashMap.put("id", id);
eventHashMap.put("osdAlarmState", String.valueOf(osdAlarmState));
eventHashMap.put("osdAlarmStateStr", mUtil.alarmStatusToString(osdAlarmState));
eventHashMap.put("dataTime", dataTime);
eventHashMap.put("type", typeStr);
eventHashMap.put("subType", subType);
eventHashMap.put("desc", desc);
mRemoteEventsList.add(eventHashMap);
}
Log.v(TAG, "getRemoteEvents() - set mRemoteEventsList(). Updating UI");
updateUi();
} catch (JSONException e) {
Log.e(TAG, "getRemoteEvents(): Error Parsing remoteEventsObj: " + e.getMessage());
mUtil.showToast("Error Parsing remoteEventsObj - this should not happen!!!");
mRemoteEventsList = null;
}
//Log.v(TAG, "getRemoteEvents(): mRemoteEventsList = " + mRemoteEventsList.toString());
}
});
}
private void updateUi() {
Log.i(TAG, "updateUi()");
boolean stopUpdating = true;
TextView tv;
Button btn;
// Local Database Information
if (mLm != null) {
mLm.getLocalEventsCount(true, (Long eventCount) -> {
TextView tv1 = (TextView) findViewById(R.id.num_local_events_tv);
tv1.setText(String.format("%d", eventCount));
});
mLm.getLocalDatapointsCount((Long datapointsCount) -> {
TextView tv2 = (TextView) findViewById(R.id.num_local_datapoints_tv);
tv2.setText(String.format("%d", datapointsCount));
});
} else {
stopUpdating = false;
}
// Local Database ListView
if (mEventsList != null) {
ListView lv = (ListView) findViewById(R.id.eventLogListView);
ListAdapter adapter = new SimpleAdapter(LogManagerControlActivity.this, mEventsList, R.layout.log_entry_layout,
new String[]{"dataTime", "status", "uploaded"},
new int[]{R.id.event_date, R.id.event_alarmState, R.id.event_uploaded});
lv.setAdapter(adapter);
//Log.v(TAG,"eventsList="+mEventsList);
} else {
stopUpdating = false;
}
// SysLog ListView
if (mSysLogList != null && mUpdateSysLog) {
ListView lv = (ListView) findViewById(R.id.sysLogListView);
ListAdapter adapter = new SimpleAdapter(LogManagerControlActivity.this, mSysLogList, R.layout.syslog_entry_layout,
new String[]{"dataTime", "logLevel", "dataJSON"},
new int[]{R.id.syslog_entry_date_tv, R.id.syslog_level_tv, R.id.syslog_entry_text_tv});
lv.setAdapter(adapter);
//Log.v(TAG,"eventsList="+mEventsList);
mUpdateSysLog = false;
}
// Remote Database List View
if (mRemoteEventsList != null) {
ListView lv = (ListView) findViewById(R.id.remoteEventsLv);
ListAdapter adapter = new RemoteEventsAdapter(LogManagerControlActivity.this, mRemoteEventsList, R.layout.log_entry_layout_remote,
new String[]{"id", "dataTime", "type", "subType", "osdAlarmStateStr", "desc"},
new int[]{R.id.event_id_remote_tv, R.id.event_date_remote_tv, R.id.event_type_remote_tv, R.id.event_subtype_remote_tv,
R.id.event_alarmState_remote_tv, R.id.event_notes_remote_tv});
lv.setAdapter(adapter);
//Log.i(TAG,"adapter[0]="+adapter.getItem(0));
//Log.i(TAG,"adapter[3]="+adapter.getItem(3));
} else {
//mUtil.showToast("No Remote Events");
Log.i(TAG, "UpdateUi: No Remote Events");
stopUpdating = false;
}
// Remote Database Information
if (mLm != null) {
tv = (TextView) findViewById(R.id.authStatusTv);
btn = (Button) findViewById(R.id.auth_button);
if (mLm.mWac.isLoggedIn()) {
tv.setText(getString(R.string.logged_in_with_token));
btn.setText(getString(R.string.logout));
} else {
tv.setText(getString(R.string.not_authenticated));
btn.setText(getString(R.string.login));
}
} else {
stopUpdating = false;
}
// Note we do not really stop updating the UI, just change from the fast update period to the slow update period
// to save hammering the databases once the UI has been populated once.
if (stopUpdating) {
stopUiTimer();
startUiTimer(mUiTimerPeriodSlow);
}
} //updateUi();
public void onRadioButtonClicked(View view) {
LinearLayout localDataLl = (LinearLayout) findViewById(R.id.local_data_ll);
LinearLayout sharedDataLl = (LinearLayout) findViewById(R.id.shared_data_ll);
LinearLayout syslogLl = (LinearLayout) findViewById(R.id.syslog_ll);
// Is the button now checked?
boolean checked = ((RadioButton) view).isChecked();
// Check which radio button was clicked
switch (view.getId()) {
case R.id.local_data_rb:
if (checked) {
// Switch to the local data view
localDataLl.setVisibility(View.VISIBLE);
sharedDataLl.setVisibility(View.GONE);
syslogLl.setVisibility(View.GONE);
}
break;
case R.id.shared_data_rb:
if (checked) {
// Switch to the local data view
localDataLl.setVisibility(View.GONE);
sharedDataLl.setVisibility(View.VISIBLE);
syslogLl.setVisibility(View.GONE);
}
break;
case R.id.syslog_rb:
if (checked) {
// Switch to the local data view
localDataLl.setVisibility(View.GONE);
sharedDataLl.setVisibility(View.GONE);
syslogLl.setVisibility(View.VISIBLE);
}
break;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.i(TAG, "onOptionsItemSelected() : " + item.getItemId() + " selected");
switch (item.getItemId()) {
case R.id.action_authenticate_api:
Log.i(TAG, "action_autheticate_api");
try {
Intent i = new Intent(
getApplicationContext(),
AuthenticateActivity.class);
this.startActivity(i);
} catch (Exception ex) {
Log.i(TAG, "exception starting export activity " + ex.toString());
}
return true;
case R.id.pruneDatabaseMenuItem:
Log.i(TAG, "action_pruneDatabase");
onPruneBtn.onClick(null);
return true;
case R.id.action_report_seizure:
Log.i(TAG, "action_report_seizure");
try {
Intent intent = new Intent(
getApplicationContext(),
ReportSeizureActivity.class);
this.startActivity(intent);
} catch (Exception ex) {
Log.i(TAG, "exception starting Report Seizure activity " + ex.toString());
}
return true;
case R.id.action_settings:
Log.i(TAG, "action_settings");
try {
Intent prefsIntent = new Intent(
getApplicationContext(),
PrefActivity.class);
this.startActivity(prefsIntent);
} catch (Exception ex) {
Log.i(TAG, "exception starting settings activity " + ex.toString());
}
return true;
case R.id.action_mark_unknown:
Log.i(TAG, "action_mark_unknown");
new AlertDialog.Builder(this)
.setTitle(R.string.mark_unverified_events_unknown_dialog_title)
.setMessage(R.string.mark_unverified_events_unknown_dialog_message)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
mLm.mWac.markUnverifiedEventsAsUnknown();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
default:
return super.onOptionsItemSelected(item);
}
}
View.OnClickListener onAuth =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onAuth");
Intent i;
i = new Intent(mContext, AuthenticateActivity.class);
startActivity(i);
}
};
View.OnClickListener onPruneBtn =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onPruneBtn");
// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("Prune Database");
builder.setMessage(String.format("This will remove all data from the database that is more than %d days old."
+ "\nThis can NOT be undone.\nAre you sure?", mLm.mDataRetentionPeriod));
builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mLm.pruneLocalDb();
dialog.dismiss();
}
});
builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
alert.show();
}
};
View.OnClickListener onReportSeizureBtn =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onReportSeizureBtn");
Intent i;
i = new Intent(mContext, ReportSeizureActivity.class);
startActivity(i);
}
};
View.OnClickListener onRemoteDbBtn =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onRemoteDbBtn");
Intent i;
i = new Intent(mContext, RemoteDbActivity.class);
startActivity(i);
}
};
View.OnClickListener onRefreshBtn =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onRefreshBtn");
initialiseServiceConnection();
}
};
AdapterView.OnItemClickListener onEventListClick =
new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
Log.v(TAG, "onItemClicKListener() - Position=" + position + ", id=" + id);// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
HashMap<String, String> eventObj = (HashMap<String, String>) adapter.getItemAtPosition(position);
String eventId = eventObj.get("uploaded");
Log.d(TAG, "onItemClickListener(): eventId=" + eventId + ", eventObj=" + eventObj);
if (eventId != null) {
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
i.putExtra("eventId", eventId);
startActivity(i);
} else {
mUtil.showToast("You Must Wait for Event to Upload before Editing it");
}
}
};
AdapterView.OnItemClickListener onRemoteEventListClick =
new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
Log.v(TAG, "onRemoteEventList Click() - Position=" + position + ", id=" + id);// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
HashMap<String, String> eventObj = (HashMap<String, String>) adapter.getItemAtPosition(position);
String eventId = eventObj.get("id");
Log.d(TAG, "onItemClickListener(): eventId=" + eventId + ", eventObj=" + eventObj);
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
i.putExtra("eventId", eventId);
startActivity(i);
}
};
/*
* Start the timer that will update the user interface every 5 seconds..
*/
private void startUiTimer(Integer uiTimerPeriod) {
if (mUiTimer != null) {
Log.v(TAG, "startUiTimer -timer already running - cancelling it");
mUiTimer.cancel();
mUiTimer = null;
}
Log.v(TAG, "startUiTimer() - starting UiTimer");
mUiTimer =
new UiTimer(uiTimerPeriod, 1000);
mUiTimer.start();
}
/*
* Cancel the remote logging timer to prevent attempts to upload to remote database.
*/
public void stopUiTimer() {
if (mUiTimer != null) {
Log.v(TAG, "stopUiTimer(): cancelling UI timer");
mUiTimer.cancel();
mUiTimer = null;
}
}
/**
* Upload recorded data to the remote database periodically.
*/
private class UiTimer extends CountDownTimer {
public UiTimer(long startTime, long interval) {
super(startTime, interval);
}
@Override
public void onTick(long l) {
// Do Nothing
}
@Override
public void onFinish() {
//Log.v(TAG, "UiTimer - onFinish - Updating UI");
updateUi();
// Restart this timer.
if (mUiTimer != null)
start();
}
}
private class RemoteEventsAdapter extends SimpleAdapter {
/**
* Constructor
*
* @param context The context where the View associated with this SimpleAdapter is running
* @param data A List of Maps. Each entry in the List corresponds to one row in the list. The
* Maps contain the data for each row, and should include all the entries specified in
* "from"
* @param resource Resource identifier of a view layout that defines the views for this list
* item. The layout file should include at least those named views defined in "to"
* @param from A list of column names that will be added to the Map associated with each
* item.
* @param to The views that should display column in the "from" parameter. These should all be
* TextViews. The first N views in this list are given the values of the first N columns
*/
public RemoteEventsAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
Map<String, ?> dataItem = (Map<String, ?>) getItem(position);
Log.v(TAG, "getView() " + dataItem.toString());
switch (dataItem.get("type").toString()) {
case "null":
case "":
v.setBackgroundColor(Color.parseColor("#ffaaaa"));
break;
case "Seizure":
v.setBackgroundColor(Color.parseColor("#ff6060"));
break;
default:
v.setBackgroundColor(Color.TRANSPARENT);
}
// Convert date format to something more readable.
TextView tv = (TextView) v.findViewById(R.id.event_date_remote_tv);
Date dataTime = null;
String dateStr = (String) dataItem.get("dataTime");
dataTime = mUtil.string2date(dateStr);
if (dataTime != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
tv.setText(dateFormat.format(dataTime));
} else {
tv.setText("---");
}
return (v);
}
}
}

View File

@@ -25,7 +25,8 @@
package uk.org.openseizuredetector;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
@@ -36,24 +37,20 @@ import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Button;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.MenuCompat;
//MPAndroidChart
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
@@ -63,6 +60,14 @@ import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.utils.ValueFormatter;
import com.rohitss.uceh.UCEHandler;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
//MPAndroidChart
public class MainActivity extends AppCompatActivity {
static final String TAG = "MainActivity";
private int okColour = Color.BLUE;
@@ -80,6 +85,7 @@ public class MainActivity extends AppCompatActivity {
final Handler serverStatusHandler = new Handler();
Messenger messenger = new Messenger(new ResponseHandler());
Timer mUiTimer;
private Context mContext;
/**
* Called when the activity is first created.
@@ -87,7 +93,7 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG,"onCreate()");
Log.i(TAG, "onCreate()");
// Set our custom uncaught exception handler to report issues.
//Thread.setDefaultUncaughtExceptionHandler(new OsdUncaughtExceptionHandler(MainActivity.this));
@@ -96,18 +102,19 @@ public class MainActivity extends AppCompatActivity {
.build();
//int i = 5/0; // Force exception to test handler.
mUtil = new OsdUtil(this,serverStatusHandler);
mConnection = new SdServiceConnection(this);
mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler);
mConnection = new SdServiceConnection(getApplicationContext());
mUtil.writeToSysLogFile("");
mUtil.writeToSysLogFile("* MainActivity Started *");
mUtil.writeToSysLogFile("MainActivity.onCreate()");
mContext = this;
// Initialise the User Interface
setContentView(R.layout.main);
/* Force display of overflow menu - from stackoverflow
* "how to force use of..."
*/
/* Force display of overflow menu - from stackoverflow
* "how to force use of..."
*/
try {
Log.v(TAG, "trying menubar fiddle...");
ViewConfiguration config = ViewConfiguration.get(this);
@@ -134,15 +141,14 @@ public class MainActivity extends AppCompatActivity {
Log.v(TAG, "acceptAlarmButton.onClick()");
if (mConnection.mBound) {
if ((mConnection.mSdServer.mSmsTimer != null)
&& (mConnection.mSdServer.mSmsTimer.mTimeLeft > 0 )){
Log.v(TAG, "acceptAlarmButton.onClick() - Stopping SMS Timer");
&& (mConnection.mSdServer.mSmsTimer.mTimeLeft > 0)) {
Log.i(TAG, "acceptAlarmButton.onClick() - Stopping SMS Timer");
mUtil.showToast(getString(R.string.SMSAlarmCancelledMsg));
mConnection.mSdServer.stopSmsTimer();
}
else {
} else {
Log.v(TAG, "acceptAlarmButton.onClick() - Accepting Alarm");
mConnection.mSdServer.acceptAlarm();
}
}
}
}
});
@@ -158,7 +164,74 @@ public class MainActivity extends AppCompatActivity {
}
});
// Deal with the 'Raise Alarm'
button = (Button) findViewById(R.id.manualAlarmButton);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.v(TAG, "manualAlarmButton.onClick()");
// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
//AlertDialog.Builder builder = new AlertDialog.Builder(getBaseContext());
//builder.setTitle("Raise Alarm");
//builder.setMessage(String.format("Raise a Seizure Detected Alarm NOW?"));
//builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
if (mConnection.mBound) {
mConnection.mSdServer.raiseManualAlarm();
}
// dialog.dismiss();
// }
//});
//builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// dialog.dismiss();
// }
//});
//AlertDialog alert = builder.create();
//if (!(this).isFinishing()) {
// alert.show();
//}
}
});
// The background service might ask us to show the data sharing dialog if data sharing is not working correctly
String actionStr = getIntent().getAction();
if (actionStr != null) {
Log.i(TAG, "onCreate() - action=" + actionStr);
if (actionStr.equals("showDataSharingDialog")) {
showDataSharingDialog();
}
} else {
Log.i(TAG, "onCreate - action is null - starting normally");
}
}
@Override
protected void onNewIntent(Intent intent) {
String actionStr;
Log.i(TAG, "onNewIntent");
Bundle extras = intent.getExtras();
// The background service might ask us to show the data sharing dialog if data sharing is not working correctly
actionStr = getIntent().getAction();
if (actionStr != null) {
Log.i(TAG, "onNewIntent() - action=" + actionStr);
if (actionStr.equals("showDataSharingDialog")) {
showDataSharingDialog();
}
} else {
if (extras != null) {
actionStr = extras.getString("action");
if (actionStr.equals("showDataSharingDialog")) {
showDataSharingDialog();
}
Log.i(TAG, "onNewIntent - extra actionstr is "+actionStr);
} else {
Log.i(TAG, "onNewIntent - extra actionstr is null - starting normally");
}
}
}
/**
@@ -168,6 +241,7 @@ public class MainActivity extends AppCompatActivity {
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "onCreateOptionsMenu()");
getMenuInflater().inflate(R.menu.main_activity_actions, menu);
MenuCompat.setGroupDividerEnabled(menu, true);
//mOptionsMenu = menu;
//if (mConnection.mSdServer.mSdDataSourceName != "Pebble") {
// Log.v(TAG,"Disabling Pebble Specific Menu Items");
@@ -191,7 +265,7 @@ public class MainActivity extends AppCompatActivity {
mConnection.mSdServer.mSdDataSource.installWatchApp();
return true;
case R.id.action_accept_alarm:
case R.id.action_accept_alarm:
Log.i(TAG, "action_accept_alarm");
if (mConnection.mBound) {
mConnection.mSdServer.acceptAlarm();
@@ -202,14 +276,14 @@ public class MainActivity extends AppCompatActivity {
Log.i(TAG, "action_sart_stop");
if (mConnection.mBound) {
Log.i(TAG, "Stopping Server");
mUtil.unbindFromServer(this, mConnection);
mUtil.unbindFromServer(getApplicationContext(), mConnection);
stopServer();
} else {
Log.i(TAG, "Starting Server");
startServer();
// and bind to it so we can see its data
Log.i(TAG, "Binding to Server");
mUtil.bindToServer(this, mConnection);
mUtil.bindToServer(getApplicationContext(), mConnection);
}
return true;
/* fault beep test does not work with fault timer, so disable test option.
@@ -238,24 +312,44 @@ public class MainActivity extends AppCompatActivity {
mConnection.mSdServer.sendSMSAlarm();
}
return true;
case R.id.action_test_phone_alarm:
/*case R.id.action_test_phone_alarm:
Log.i(TAG, "action_test_phone_alarm");
if (mConnection.mBound) {
mConnection.mSdServer.sendPhoneAlarm();
}
return true;
case R.id.action_export:
Log.i(TAG, "action_export");
*/
case R.id.action_authenticate_api:
Log.i(TAG, "action_autheticate_api");
try {
Intent i = new Intent(
MainActivity.this,
DBQueryActivity.class);
AuthenticateActivity.class);
this.startActivity(i);
} catch (Exception ex) {
Log.i(TAG, "exception starting export activity " + ex.toString());
}
return true;
case R.id.action_logs:
case R.id.action_about_datasharing:
Log.i(TAG, "action_about_datasharing");
showDataSharingDialog();
return true;
/*
case R.id.action_export:
Log.i(TAG, "action_export");
try {
Intent i = new Intent(
MainActivity.this,
ExportDataActivity.class);
this.startActivity(i);
} catch (Exception ex) {
Log.i(TAG, "exception starting export activity " + ex.toString());
}
return true;
*/
/* case R.id.action_logs:
Log.i(TAG, "action_logs");
try {
String url = "http://"
@@ -272,17 +366,29 @@ public class MainActivity extends AppCompatActivity {
Log.i(TAG, "exception starting log manager activity " + ex.toString());
}
return true;
*/
case R.id.action_logmanager:
Log.i(TAG, "action_logmanager");
try {
Intent intent = new Intent(
MainActivity.this,
LogManagerActivity.class);
LogManagerControlActivity.class);
this.startActivity(intent);
} catch (Exception ex) {
Log.i(TAG, "exception starting log manager activity " + ex.toString());
}
return true;
case R.id.action_report_seizure:
Log.i(TAG, "action_report_seizure");
try {
Intent intent = new Intent(
MainActivity.this,
ReportSeizureActivity.class);
this.startActivity(intent);
} catch (Exception ex) {
Log.i(TAG, "exception starting Report Seizure activity " + ex.toString());
}
return true;
case R.id.action_settings:
Log.i(TAG, "action_settings");
try {
@@ -307,7 +413,7 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
Log.i(TAG,"onStart()");
Log.i(TAG, "onStart()");
mUtil.writeToSysLogFile("MainActivity.onStart()");
SharedPreferences SP = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
@@ -321,9 +427,9 @@ public class MainActivity extends AppCompatActivity {
if (mUtil.isServerRunning()) {
mUtil.writeToSysLogFile("MainActivity.onStart - Binding to Server");
mUtil.bindToServer(this, mConnection);
mUtil.bindToServer(getApplicationContext(), mConnection);
} else {
Log.i(TAG,"onStart() - Server Not Running");
Log.i(TAG, "onStart() - Server Not Running");
mUtil.writeToSysLogFile("MainActivity.onStart - Server Not Running");
}
// start timer to refresh user interface every second.
@@ -341,9 +447,9 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onStop() {
super.onStop();
Log.i(TAG,"onStop() - unbinding from server");
Log.i(TAG, "onStop() - unbinding from server");
mUtil.writeToSysLogFile("MainActivity.onStop()");
mUtil.unbindFromServer(this, mConnection);
mUtil.unbindFromServer(getApplicationContext(), mConnection);
mUiTimer.cancel();
}
@@ -402,7 +508,7 @@ public class MainActivity extends AppCompatActivity {
tv.setBackgroundColor(okColour);
tv.setTextColor(okTextColour);
tv = (TextView) findViewById(R.id.serverIpTv);
tv.setText(getString(R.string.AccessServerAt)+" http://"
tv.setText(getString(R.string.AccessServerAt) + " http://"
+ mUtil.getLocalIpAddress()
+ ":8080");
tv.setBackgroundColor(okColour);
@@ -460,11 +566,17 @@ public class MainActivity extends AppCompatActivity {
// Pebble Connected Phrase - use for HR if active instead.
tv = (TextView) findViewById(R.id.pebbleTv);
if (mConnection.mSdServer.mSdData.mHRAlarmActive) {
tv.setText(getString(R.string.HR_Equals) + mConnection.mSdServer.mSdData.mHR);
if (mConnection.mSdServer.mSdData.mHRAlarmStanding) {
if (mConnection.mSdServer.mSdData.mO2Sat>0) {
tv.setText(getString(R.string.HR_Equals) + mConnection.mSdServer.mSdData.mHR + " bpm\n"
+ "O2 Sat = " + mConnection.mSdServer.mSdData.mO2Sat + "%");
} else {
tv.setText(getString(R.string.HR_Equals) + mConnection.mSdServer.mSdData.mHR + " bpm\n"
+ "O2 Sat = ---%");
}
if (mConnection.mSdServer.mSdData.mHRAlarmStanding || mConnection.mSdServer.mSdData.mO2SatAlarmStanding) {
tv.setBackgroundColor(alarmColour);
tv.setTextColor(alarmTextColour);
} else if (mConnection.mSdServer.mSdData.mHRFaultStanding) {
} else if (mConnection.mSdServer.mSdData.mHRFaultStanding || mConnection.mSdServer.mSdData.mO2SatFaultStanding) {
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
} else {
@@ -507,6 +619,65 @@ public class MainActivity extends AppCompatActivity {
tv.setBackgroundColor(okColour);
tv.setTextColor(okTextColour);
}
////////////////////////////////////////////////////////////
// Populate the Data Sharing Status Box
// We start off with it set to OK, then check for several different abnormal conditions
// in turn - the last one that is active is the one that is displayed.
tv = (TextView) findViewById(R.id.remoteDbTv);
tv.setText(getString(R.string.data_sharing_status)
+ ": "
+ getString(R.string.data_sharing_setup_ok));
tv.setBackgroundColor(okColour);
tv.setTextColor(okTextColour);
if (!mConnection.mSdServer.mLm.mWac.checkServerConnection()) {
// Problem connecting to server
tv = (TextView) findViewById(R.id.remoteDbTv);
tv.setText(getString(R.string.data_sharing_status)
+ ": "
+ getString(R.string.error_connecting_to_server));
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
}
if (!mConnection.mSdServer.mLogDataRemoteMobile && mUtil.isMobileDataActive()) {
// We are on mobile internet but we are set to not upload over mobile data.
tv.setText(getString(R.string.data_sharing_status)
+ ": "
+ getString(R.string.not_updating_mobile));
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
}
if (!mUtil.isNetworkConnected()) {
// No network connection
tv.setText(getString(R.string.data_sharing_status)
+ ": "
+ getString(R.string.not_updating_no_network));
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
}
if (!mConnection.mSdServer.mLm.mWac.isLoggedIn()) {
// Not Logged In
tv.setText(getString(R.string.data_sharing_status)
+ ": "
+ getString(R.string.not_logged_in));
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
}
if (!mConnection.mSdServer.mLogData) {
// Not set to share data
tv.setText(getString(R.string.data_sharing_status)
+ ": "
+ getString(R.string.not_sharing_logged_data));
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
}
/////////////////////////////////////////////////////
// Set ProgressBars to show margin to alarm.
long powerPc;
if (mConnection.mSdServer.mSdData.alarmThresh != 0)
@@ -532,9 +703,9 @@ public class MainActivity extends AppCompatActivity {
specRatio = 0;
((TextView) findViewById(R.id.powerTv)).setText(getString(R.string.PowerEquals) + mConnection.mSdServer.mSdData.roiPower +
" ("+ getString(R.string.Threshold) + "=" + mConnection.mSdServer.mSdData.alarmThresh + ")");
" (" + getString(R.string.Threshold) + "=" + mConnection.mSdServer.mSdData.alarmThresh + ")");
((TextView) findViewById(R.id.spectrumTv)).setText(getString(R.string.SpectrumRatioEquals) + specRatio +
" ("+ getString(R.string.Threshold) + "=" + mConnection.mSdServer.mSdData.alarmRatioThresh + ")");
" (" + getString(R.string.Threshold) + "=" + mConnection.mSdServer.mSdData.alarmRatioThresh + ")");
ProgressBar pb;
Drawable pbDrawable;
@@ -589,21 +760,20 @@ public class MainActivity extends AppCompatActivity {
tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.pebbleTv);
tv.setText(getString(R.string.HR_Equals)+"---");
tv.setText(getString(R.string.HR_Equals) + " --- bpm\nO2 Sat = --- %");
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.appTv);
tv.setText(getString(R.string.WatchApp)+" ----");
tv.setText(getString(R.string.WatchApp) + " ----");
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.battTv);
tv.setText(getString(R.string.WatchBatteryEquals)+" ---%");
tv.setText(getString(R.string.WatchBatteryEquals) + " ---%");
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
}
} else { // Not bound to server
tv = (TextView) findViewById(R.id.alarmTv);
tv.setText(R.string.Dashes);
@@ -620,17 +790,22 @@ public class MainActivity extends AppCompatActivity {
tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.pebbleTv);
tv.setText(getString(R.string.HR_Equals)+"---");
tv.setText(getString(R.string.HR_Equals) + "---");
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.appTv);
tv.setText(getString(R.string.WatchApp)+" -----");
tv.setText(getString(R.string.WatchApp) + " -----");
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.battTv);
tv.setText(getString(R.string.WatchBatteryEquals)+" ---%");
tv.setText(getString(R.string.WatchBatteryEquals) + " ---%");
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.remoteDbTv);
tv.setText("---");
tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour);
}
@@ -647,12 +822,12 @@ public class MainActivity extends AppCompatActivity {
&& (mConnection.mSdServer.mSmsTimer.mTimeLeft > 0)) {
acceptAlarmButton.setText(getString(R.string.SMSWillBeSentIn) + " " +
mConnection.mSdServer.mSmsTimer.mTimeLeft / 1000 +
" s - "+getString(R.string.Cancel));
" s - " + getString(R.string.Cancel));
acceptAlarmButton.setBackgroundColor(alarmColour);
acceptAlarmButton.setEnabled(true);
} else {
acceptAlarmButton.setText(R.string.AcceptAlarm);
acceptAlarmButton.setBackgroundColor(Color.DKGRAY);
acceptAlarmButton.setBackgroundColor(Color.GRAY);
if (mConnection.mBound)
if ((mConnection.mSdServer.isLatchAlarms())
|| mConnection.mSdServer.mSdData.mFallActive) {
@@ -673,15 +848,17 @@ public class MainActivity extends AppCompatActivity {
if (mConnection.mBound)
if (mConnection.mSdServer.isAudibleCancelled()) {
cancelAudibleButton.setText(getString(R.string.AudibleAlarmsCancelledFor)
+ mConnection.mSdServer.
+ " " + mConnection.mSdServer.
cancelAudibleTimeRemaining()
+ " sec."
+ getString(R.string.PressToReEnable));
+ " sec");
cancelAudibleButton.setEnabled(true);
} else {
if (mConnection.mSdServer.mAudibleAlarm) {
cancelAudibleButton.setText(R.string.CancelAudibleAlarms);
cancelAudibleButton.setEnabled(true);
} else {
cancelAudibleButton.setText(R.string.AudibleAlarmsOff);
cancelAudibleButton.setEnabled(false);
}
}
@@ -696,17 +873,16 @@ public class MainActivity extends AppCompatActivity {
ArrayList<String> xVals = new ArrayList<String>();
ArrayList<BarEntry> yBarVals = new ArrayList<BarEntry>();
for (int i = 0; i < 10; i++) {
xVals.add(i+"-"+(i+1)+" Hz");
xVals.add(i + "-" + (i + 1) + " Hz");
if (mConnection.mSdServer != null) {
yBarVals.add(new BarEntry(mConnection.mSdServer.mSdData.simpleSpec[i], i));
}
else {
yBarVals.add(new BarEntry(i,i));
} else {
yBarVals.add(new BarEntry(i, i));
}
}
// create a dataset and give it a type
BarDataSet barDataSet = new BarDataSet(yBarVals,"Spectrum");
BarDataSet barDataSet = new BarDataSet(yBarVals, "Spectrum");
try {
int[] barColours = new int[10];
for (int i = 0; i < 10; i++) {
@@ -718,20 +894,20 @@ public class MainActivity extends AppCompatActivity {
}
}
barDataSet.setColors(barColours);
} catch (NullPointerException e){
Log.e(TAG,"Null pointer exception setting bar colours");
} catch (NullPointerException e) {
Log.e(TAG, "Null pointer exception setting bar colours");
}
barDataSet.setBarSpacePercent(20f);
barDataSet.setBarShadowColor(Color.WHITE);
BarData barData = new BarData(xVals,barDataSet);
BarData barData = new BarData(xVals, barDataSet);
barData.setValueFormatter(new ValueFormatter() {
@Override
public String getFormattedValue(float v) {
DecimalFormat format = new DecimalFormat("####");
return format.format(v);
}
});
mChart.setData(barData);
@Override
public String getFormattedValue(float v) {
DecimalFormat format = new DecimalFormat("####");
return format.format(v);
}
});
mChart.setData(barData);
// format the axes
XAxis xAxis = mChart.getXAxis();
@@ -764,7 +940,7 @@ public class MainActivity extends AppCompatActivity {
try {
mChart.getLegend().setEnabled(false);
} catch (NullPointerException e) {
Log.e(TAG,"Null Pointer Exception setting legend");
Log.e(TAG, "Null Pointer Exception setting legend");
}
mChart.invalidate();
@@ -775,14 +951,14 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onPause() {
super.onPause();
Log.i(TAG,"onPause()");
Log.i(TAG, "onPause()");
mUtil.writeToSysLogFile("MainActivity.onPause()");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG,"onResume()");
Log.i(TAG, "onResume()");
mUtil.writeToSysLogFile("MainActivity.onResume()");
}
@@ -795,16 +971,69 @@ public class MainActivity extends AppCompatActivity {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.drawable.icon_24x24);
builder.setTitle("OpenSeizureDetector V" + versionName);
builder.setNeutralButton(getString(R.string.closeBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.setPositiveButton("Privacy Policy", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
String url = OsdUtil.PRIVACY_POLICY_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
dialog.cancel();
}
});
builder.setNegativeButton("Data Sharing", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
String url = OsdUtil.DATA_SHARING_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
dialog.cancel();
}
});
builder.setView(aboutView);
builder.create();
builder.show();
}
private void showDataSharingDialog() {
mUtil.writeToSysLogFile("MainActivity.showDataSharingDialog()");
View aboutView = getLayoutInflater().inflate(R.layout.data_sharing_dialog_layout, null, false);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.drawable.datasharing_fault_24x24);
builder.setTitle("OpenSeizureDetector Data Sharing");
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.login), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "dataSharingDialog.positiveButton.onClick()");
try {
Intent i = new Intent(
MainActivity.this,
AuthenticateActivity.class);
mContext.startActivity(i);
} catch (Exception ex) {
Log.i(TAG, "exception starting activity " + ex.toString());
}
}
});
builder.setView(aboutView);
builder.create();
builder.show();
}
static class ResponseHandler extends Handler {
@Override
public void handleMessage(Message message) {
Log.i(TAG, "Message=" + message.toString());
}
}
}
}

View File

@@ -1,6 +1,5 @@
package uk.org.openseizuredetector;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -11,7 +10,8 @@ import android.os.Environment;
import android.os.Looper;
import android.os.StatFs;
import android.util.Log;
import android.view.WindowManager;
import androidx.appcompat.app.AlertDialog;
import java.io.File;
import java.io.PrintWriter;

View File

@@ -27,93 +27,75 @@ package uk.org.openseizuredetector;
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.text.format.Time;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.apache.http.conn.util.InetAddressUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.AbstractList;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.RandomAccess;
import java.util.concurrent.RunnableFuture;
import java.util.HashMap;
import java.util.function.Consumer;
/**
* OsdUtil - OpenSeizureDetector Utilities
* Deals with starting and stopping the background service and binding to it to receive data.
*/
public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallback {
public class OsdUtil {
public final static String PRIVACY_POLICY_URL = "https://www.openseizuredetector.org.uk/?page_id=1415";
public final static String DATA_SHARING_URL = "https://www.openseizuredetector.org.uk/?page_id=1818";
private final String SYSLOG = "SysLog";
private final String ALARMLOG = "AlarmLog";
private final String DATALOG = "DataLog";
private final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.WAKE_LOCK,
};
private final String[] SMS_PERMISSIONS = {
Manifest.permission.SEND_SMS,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_PHONE_STATE,
};
/**
* Based on http://stackoverflow.com/questions/7440473/android-how-to-check-if-the-intent-service-is-still-running-or-has-stopped-running
*/
private Context mContext;
private static Context mContext;
private Handler mHandler;
private String TAG = "OsdUtil";
private static String TAG = "OsdUtil";
private boolean mLogAlarms = true;
private boolean mLogSystem = true;
private boolean mLogData = true;
private boolean mPermissionsRequested = false;
private boolean mSMSPermissionsRequested = false;
private static final String mSysLogTableName = "SysLog";
//private LogManager mLm;
static private SQLiteDatabase mSysLogDb = null; // SQLite Database for data and log entries.
private final static Long mMinPruneInterval = new Long(5 * 60 * 1000); // minimum time between syslog pruning is 5 minutes
private static Long mLastPruneMillis = new Long(0); // Record of the last time we pruned the syslog db.
private static int mNbound = 0;
@@ -121,6 +103,9 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
mContext = context;
mHandler = handler;
updatePrefs();
//Log.i(TAG,"Creating Log Manager instance");
//mLm = new LogManager(mContext,false,false,null,0,0,false,0);
openDb();
writeToSysLogFile("OsdUtil() - initialised");
}
@@ -136,8 +121,8 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
try {
mLogAlarms = SP.getBoolean("LogAlarms", true);
Log.v(TAG, "updatePrefs() - mLogAlarms = " + mLogAlarms);
mLogData = SP.getBoolean("LogData", false);
Log.v(TAG, "updatePrefs() - mLogData = " + mLogData);
mLogData = SP.getBoolean("LogData", true);
Log.v(TAG, "OsdUtil.updatePrefs() - mLogData = " + mLogData);
mLogSystem = SP.getBoolean("LogSystem", true);
Log.v(TAG, "updatePrefs() - mLogSystem = " + mLogSystem);
@@ -170,7 +155,7 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
}
}
if (nServers != 0) {
Log.v(TAG, "isServerRunning() - " + nServers + " instances are running");
//Log.v(TAG, "isServerRunning() - " + nServers + " instances are running");
return true;
} else
return false;
@@ -199,7 +184,7 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
* Stop the SdServer service
*/
public void stopServer() {
Log.d(TAG, "OsdUtil.stopServer() - stopping Server... - mNbound=" + mNbound);
Log.i(TAG, "OsdUtil.stopServer() - stopping Server... - mNbound=" + mNbound);
writeToSysLogFile("stopserver() - stopping server");
// then send an Intent to stop the service.
@@ -213,7 +198,7 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
/**
* bind an activity to to an already running server.
*/
public void bindToServer(Activity activity, SdServiceConnection sdServiceConnection) {
public void bindToServer(Context activity, SdServiceConnection sdServiceConnection) {
Log.i(TAG, "OsdUtil.bindToServer() - binding to SdServer");
writeToSysLogFile("bindToServer() - binding to SdServer");
Intent intent = new Intent(sdServiceConnection.mContext, SdServer.class);
@@ -225,7 +210,7 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
/**
* unbind an activity from server
*/
public void unbindFromServer(Activity activity, SdServiceConnection sdServiceConnection) {
public void unbindFromServer(Context activity, SdServiceConnection sdServiceConnection) {
// unbind this activity from the service if it is bound.
if (sdServiceConnection.mBound) {
Log.i(TAG, "unbindFromServer() - unbinding");
@@ -301,6 +286,7 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
// return true if we are using mobile data, otherwise return false
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork == null) return false;
if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
return true;
} else {
@@ -312,7 +298,11 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
// return true if we have a network connection, otherwise false.
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return (activeNetwork.isConnected());
if (activeNetwork != null) {
return (activeNetwork.isConnected());
} else {
return (false);
}
}
/**
@@ -331,16 +321,18 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
/**
* Write a message to the system log file, provided mLogSystem is true.
* Write a message to the system log database.
*
* @param msgStr
*/
public void writeToSysLogFile(String msgStr) {
if (mLogSystem)
writeToLogFile(SYSLOG, msgStr);
else
Log.v(TAG, "writeToSysLogFile - mLogSystem False so not writing");
public void writeToSysLogFile(String msgStr,String logType) {
writeLogEntryToLocalDb(msgStr,logType);
}
public void writeToSysLogFile(String msgStr) {
writeLogEntryToLocalDb(msgStr,"v");
}
/**
* Write a message to the alarm log file, provided mLogAlarms is true.
@@ -462,82 +454,289 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
}
}
public boolean arePermissionsOK() {
boolean allOk = true;
Log.v(TAG, "arePermissionsOK");
for (int i = 0; i < REQUIRED_PERMISSIONS.length; i++) {
if (ContextCompat.checkSelfPermission(mContext, REQUIRED_PERMISSIONS[i])
!= PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, REQUIRED_PERMISSIONS[i] + " Permission Not Granted");
allOk = false;
/**
* 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 allOk;
}
public boolean areSMSPermissionsOK() {
boolean allOk = true;
Log.v(TAG, "areSMSPermissionsOK()");
for (int i = 0; i < SMS_PERMISSIONS.length; i++) {
if (ContextCompat.checkSelfPermission(mContext, SMS_PERMISSIONS[i])
!= PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, SMS_PERMISSIONS[i] + " Permission Not Granted");
allOk = false;
}
}
return allOk;
return(dataTime);
}
public void requestPermissions(Activity activity) {
if (mPermissionsRequested) {
Log.i(TAG, "requestPermissions() - request already sent - not doing anything");
} else {
Log.i(TAG, "requestPermissions() - requesting permissions");
for (int i = 0; i < REQUIRED_PERMISSIONS.length; i++) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
REQUIRED_PERMISSIONS[i])) {
Log.i(TAG, "shouldShowRationale for permission" + REQUIRED_PERMISSIONS[i]);
public final int ALARM_STATUS_WARNING = 1;
public final int ALARM_STATUS_ALARM = 2;
public final int ALARM_STATUS_FALL = 3;
public final int ALARM_STATUS_MANUAL = 5;
public String alarmStatusToString(int eventAlarmStatus) {
String retVal = "Unknown";
switch (eventAlarmStatus) {
case ALARM_STATUS_WARNING: // Warning
retVal = "WARNING";
break;
case ALARM_STATUS_ALARM: // alarm
retVal = "ALARM";
break;
case ALARM_STATUS_FALL: // fall
retVal = "FALL";
break;
case ALARM_STATUS_MANUAL: // Manual alarm
retVal = "MANUAL ALARM";
break;
}
return(retVal);
}
private static boolean openDb() {
Log.d(TAG, "openDb");
try {
if (mSysLogDb == null) {
Log.i(TAG,"openDb: mSysLogDb is null - initialising");
mSysLogDb = new OsdSysLogHelper(mContext).getWritableDatabase();
} else {
Log.i(TAG,"openDb: mSysLogDb has been initialised already so not doing anything");
}
if (!checkTableExists(mSysLogDb, mSysLogTableName)) {
Log.e(TAG, "ERROR - Table "+mSysLogTableName+" does not exist");
return false;
} else {
Log.d(TAG, "table " + mSysLogTableName + " exists ok");
}
} catch (SQLException e) {
Log.e(TAG, "Failed to open Database: " + e.toString());
return false;
}
return true;
}
private static boolean checkTableExists(SQLiteDatabase osdDb, String osdTableName) {
Cursor c = null;
boolean tableExists = false;
Log.d(TAG, "checkTableExists()");
try {
c = osdDb.query(osdTableName, null,
null, null, null, null, null);
tableExists = true;
c.close();
} catch (Exception e) {
Log.d(TAG, osdTableName + " doesn't exist :(((");
}
return tableExists;
}
/**
* Write syslog string to local database
* FIXME - I am sure we should not be using raw SQL Srings to do this!
*/
public void writeLogEntryToLocalDb(String logText, String statusVal) {
Log.v(TAG, "writeLogEntryToLocalDb()");
Date curDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = dateFormat.format(curDate);
String SQLStr = "SQLStr";
try {
SQLStr = "INSERT INTO " + mSysLogTableName
+ "(dataTime, logLevel, dataJSON, uploaded)"
+ " VALUES("
+ "'" + dateStr + "',"
+ DatabaseUtils.sqlEscapeString(statusVal) + ","
+ DatabaseUtils.sqlEscapeString(logText) + ","
+ 0
+ ")";
mSysLogDb.execSQL(SQLStr);
Log.v(TAG, "syslog entry written to database: "+logText);
pruneSysLogDb();
} catch (SQLException e) {
Log.e(TAG, "writeLogEngryToLocalDb(): Error Writing Data: " + e.toString());
Log.e(TAG, "SQLStr was " + SQLStr);
}
}
/**
* Return an array list of objects representing the syslog entries in the database by calling the specified callback function.
*
* @return True on successful start or false if call fails.
*/
public boolean getSysLogList(Consumer<ArrayList<HashMap<String, String>>> callback) {
Log.v(TAG, "getSysLogList");
ArrayList<HashMap<String, String>> eventsList = new ArrayList<>();
String whereClause = "";
String[] whereArgs = {};
String[] columns = {"*"};
new SelectQueryTask(mSysLogTableName, columns, null, null,
null, null, "dataTime DESC", (Cursor cursor) -> {
Log.v(TAG, "getSysLogList - returned " + cursor);
if (cursor != null) {
Log.v(TAG, "getSysLogList - returned " + cursor.getCount() + " records");
while (!cursor.isAfterLast()) {
HashMap<String, String> event = new HashMap<>();
//event.put("id", cursor.getString(cursor.getColumnIndex("id")));
event.put("dataTime", cursor.getString(cursor.getColumnIndex("dataTime")));
String loglevel = cursor.getString(cursor.getColumnIndex("logLevel"));
event.put("loglevel", loglevel);
event.put("dataJSON", cursor.getString(cursor.getColumnIndex("dataJSON")));
//event.put("dataJSON", cursor.getString(cursor.getColumnIndex("dataJSON")));
eventsList.add(event);
cursor.moveToNext();
}
}
ActivityCompat.requestPermissions(activity,
REQUIRED_PERMISSIONS,
42);
mPermissionsRequested = true;
callback.accept(eventsList);
}).execute();
return (true);
}
/**
* Executes the sqlite query (=SELECT statement)
* Use as new SelectQueryTask(xxx,xxx,xx,xxxx).execute()
*
*/
static private class SelectQueryTask extends AsyncTask<Void, Void, Cursor> {
// Based on https://stackoverflow.com/a/21120199/2104584
String mTable;
String[] mColumns;
String mSelection;
String[] mSelectionArgs;
String mGroupBy;
String mHaving;
String mOrderBy;
Consumer<Cursor> mCallback;
//query(String table, String[] columns, String selection, String[] selectionArgs,
// String groupBy, String having, String orderBy)
SelectQueryTask(String table, String[] columns, String selection, String[] selectionArgs,
String groupBy, String having, String orderBy, Consumer<Cursor> callback) {
// list all the parameters like in normal class define
this.mTable = table;
this.mColumns = columns;
this.mSelection = selection;
this.mSelectionArgs = selectionArgs;
this.mGroupBy = groupBy;
this.mHaving = having;
this.mOrderBy = orderBy;
this.mCallback = callback;
}
@Override
protected Cursor doInBackground(Void... params) {
Log.v(TAG, "runSelect.doInBackground()");
Log.v(TAG, "SelectQueryTask.doInBackground: mTable=" + mTable + ", mColumns=" + Arrays.toString(mColumns)
+ ", mSelection=" + mSelection + ", mSelectionArgs=" + Arrays.toString(mSelectionArgs) + ", mGroupBy=" + mGroupBy
+ ", mHaving =" + mHaving + ", mOrderBy=" + mOrderBy);
try {
Cursor resultSet = mSysLogDb.query(mTable, mColumns, mSelection,
mSelectionArgs, mGroupBy, mHaving, mOrderBy);
resultSet.moveToFirst();
return (resultSet);
} catch (SQLException e) {
Log.e(TAG, "SelectQueryTask.doInBackground(): Error selecting Data: " + e.toString());
return (null);
} catch (IllegalArgumentException e) {
Log.e(TAG, "SelectQueryTask.doInBackground(): Illegal Argument Exception: " + e.toString());
return (null);
}
}
@Override
protected void onPostExecute(final Cursor result) {
mCallback.accept(result);
}
}
public void requestSMSPermissions(Activity activity) {
if (mSMSPermissionsRequested) {
Log.i(TAG, "requestSMSPermissions() - request already sent - not doing anything");
/**
* pruneSysLogDb() removes data that is older than 7 days
*/
public int pruneSysLogDb() {
//Log.v(TAG, "pruneSysLogDb()");
int retVal;
long currentDateMillis = new Date().getTime();
if (currentDateMillis > mLastPruneMillis + mMinPruneInterval) {
mLastPruneMillis = currentDateMillis;
// FIXME - change this to something sensible like 7 days after testing
long endDateMillis = currentDateMillis - 5 * 60 * 1000;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endDateStr = dateFormat.format(new Date(endDateMillis));
Log.v(TAG, "pruneSysLogDb - endDateStr=" + endDateStr);
try {
String selectStr = "DataTime<=?";
String[] selectArgs = {endDateStr};
retVal = mSysLogDb.delete(mSysLogTableName, selectStr, selectArgs);
} catch (Exception e) {
Log.e(TAG, "Error deleting log entries" + e.toString());
retVal = 0;
}
if (retVal > 0) {
Log.v(TAG, String.format("pruneSysLogDb() - deleted %d records", retVal));
}
return (retVal);
} else {
Log.i(TAG, "requestSMSPermissions() - requesting permissions");
for (int i = 0; i < SMS_PERMISSIONS.length; i++) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
SMS_PERMISSIONS[i])) {
Log.i(TAG, "shouldShowRationale for permission" + SMS_PERMISSIONS[i]);
}
}
ActivityCompat.requestPermissions(activity,
SMS_PERMISSIONS,
43);
mSMSPermissionsRequested = true;
return (0);
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
Log.i(TAG, "onRequestPermissionsResult - Permission" + permissions + " = " + grantResults);
showToast(mContext.getString(R.string.RestartingServerMsg));
stopServer();
// Wait 0.1 second to give the server chance to shutdown, then re-start it
mHandler.postDelayed(new Runnable() {
public void run() {
startServer();
}
}, 100);
public static class OsdSysLogHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "OsdSysLog.db";
private static final String TAG = "LogManager.OsdSysLogHelper";
public OsdSysLogHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.d(TAG, "OsdSysLogHelper constructor");
}
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, "onCreate - TableName=" + mSysLogTableName);
String SQLStr = "CREATE TABLE IF NOT EXISTS " + mSysLogTableName + "("
+ "id INTEGER PRIMARY KEY,"
+ "dataTime DATETIME,"
+ "logLevel TEXT,"
+ "dataJSON TEXT,"
+ "uploaded INT"
+ ");";
db.execSQL(SQLStr);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
Log.i(TAG,"onUpgrade()");
db.execSQL("Drop table if exists " + mSysLogTableName + ";");
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG,"onDowngrade()");
onUpgrade(db, oldVersion, newVersion);
}
}
}

View File

@@ -75,7 +75,8 @@ public class PrefActivity extends PreferenceActivity implements SharedPreference
.getDefaultSharedPreferences(this.getApplicationContext());
String dataSourceStr = SP.getString("DataSource", "Pebble");
Log.i(TAG, "onBuildHeaders DataSource = " + dataSourceStr);
Boolean advancedMode = SP.getBoolean("advancedMode", false);
//Boolean advancedMode = SP.getBoolean("advancedMode", false);
Boolean advancedMode = true;
Log.i(TAG, "onBuildHeaders advancedMode = " + advancedMode);
if (advancedMode) {
@@ -120,7 +121,7 @@ public class PrefActivity extends PreferenceActivity implements SharedPreference
} else {
titleStr = getResources().getString(h.titleRes);
}
Log.v(TAG, "i=" + i + ": found - " + titleStr + " looking for "+ getString(R.string.basic_settings_title));
Log.v(TAG, "i=" + i + ": found - " + titleStr + " looking for " + getString(R.string.basic_settings_title));
if (!titleStr.equals(getString(R.string.basic_settings_title))) {
Log.v(TAG, "an Advanced Mode Header, so removing it....");
target.remove(i);
@@ -143,18 +144,22 @@ public class PrefActivity extends PreferenceActivity implements SharedPreference
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
Log.i(TAG, "SharedPreference " + s + " Changed.");
// if we have enabled the SMS alarm, we may need extra permissions approving. This is handled in
// StartUpActivity, so we exit this activity and start start-up activity.
if (s.equals("SMSAlarm")) {
if (sharedPreferences.getBoolean("SMSAlarm", false) == true) {
if (mUtil.areSMSPermissionsOK() == false) {
Log.i(TAG, "onSharedPreferenceChanged(): SMS Alarm Enabled - Requesting Permissions");
mUtil.requestSMSPermissions(this);
} else {
Log.i(TAG, "OnSharedPreferenceCHanged(): SMS Permissions already granted, doing nothing");
}
Log.i(TAG, "onSharedPreferenceChanged(): SMS Alarm Enabled - Restarting start-up activity to check permissions");
Intent i;
i = new Intent(this, StartupActivity.class);
startActivity(i);
Log.i(TAG,"onSharedPreferenceChanged() - finishing PrefActivity");
finish();
return;
} else {
Log.i(TAG, "OnSharedPreferenceChanged(): SMS Alarm disabled so do not need permissions");
}
}
// For all other preference changes we just restart SdServer so it is not as alarming for the user!
//mUtil.showToast("Setting " + s + " Changed - restarting server");
mPrefChanged = true;
mUtil.stopServer();
@@ -213,6 +218,7 @@ public class PrefActivity extends PreferenceActivity implements SharedPreference
protected void onStop() {
super.onStop();
mUtil.writeToSysLogFile("PrefActvity.onStop()");
Log.i(TAG,"onStop()");
}
/**
@@ -276,6 +282,17 @@ public class PrefActivity extends PreferenceActivity implements SharedPreference
}
}
public static class LoggingPrefsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.logging_prefs);
}
}
public static class SeizureDetectorPrefsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {

View File

@@ -0,0 +1,226 @@
package uk.org.openseizuredetector;
//import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.preference.PreferenceManager;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.TextView;
import java.util.HashMap;
public class RemoteDbActivity extends AppCompatActivity {
private String TAG = "RemoteDbActivity";
private Context mContext;
private UiTimer mUiTimer;
private LogManager mLm;
private WebView mWebView;
private SdServiceConnection mConnection;
private OsdUtil mUtil;
final Handler serverStatusHandler = new Handler();
private String TOKEN_ID = "webApiAuthToken";
private String mRemtoteUrl = "https://osdapi.ddns.net/";
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_remote_db);
mUtil = new OsdUtil(getApplicationContext(), serverStatusHandler);
mConnection = new SdServiceConnection(getApplicationContext());
mUtil.bindToServer(getApplicationContext(), mConnection);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String remoteUrl = extras.getString("url");
mRemtoteUrl = remoteUrl;
Log.d(TAG, "onCreate - mRemoteUrl=" + mRemtoteUrl);
}
waitForConnection();
//mLm= new LogManager(mContext);
Button authBtn =
(Button) findViewById(R.id.auth_button);
authBtn.setOnClickListener(onAuth);
//Button pruneBtn =
// (Button) findViewById(R.id.pruneDatabaseBtn);
//pruneBtn.setOnClickListener(onPruneBtn);
mWebView = (WebView) findViewById(R.id.remote_db_webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
}
private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for
// the mConnection to bind to the service, so we delay half a second to give it chance
// to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer)
if (mConnection.mBound) {
Log.d(TAG, "waitForConnection - Bound!");
initialiseServiceConnection();
} else {
Log.v(TAG, "waitForConnection - waiting...");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
waitForConnection();
}
}, 100);
}
}
private void initialiseServiceConnection() {
mLm = mConnection.mSdServer.mLm;
mWebView.loadUrl(mRemtoteUrl, getAuthHeaders());
//mWac = mConnection.mSdServer.mLm.mWac;
}
@Override
protected void onStart() {
super.onStart();
waitForConnection();
updateUi();
//startUiTimer();
}
@Override
protected void onPause() {
super.onPause();
stopUiTimer();
}
@Override
protected void onResume() {
super.onResume();
startUiTimer();
}
private HashMap<String, String> getAuthHeaders() {
HashMap<String, String> headersMap = new HashMap<>();
String authToken = getAuthToken();
headersMap.put("Authorization", "Token "+authToken);
return (headersMap);
}
public String getAuthToken() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
String authToken = prefs.getString(TOKEN_ID, null);
return authToken;
}
private void updateUi() {
Log.v(TAG,"updateUi()");
TextView tv;
Button btn;
// Local Database Information
//tv = (TextView)findViewById(R.id.num_local_events_tv);
//int eventCount = 0;
//tv.setText(String.format("%d",eventCount));
//tv = (TextView)findViewById(R.id.num_local_datapoints_tv);
//int datapointsCount = 0;
//tv.setText(String.format("%d",datapointsCount));
// Remote Database Information
tv = (TextView)findViewById(R.id.authStatusTv);
btn = (Button)findViewById(R.id.auth_button);
if (mLm != null) {
if (mLm.mWac.isLoggedIn()) {
tv.setText("Authenticated");
btn.setText("Log Out");
} else {
tv.setText("NOT AUTHENTICATED");
btn.setText("Log In");
}
}
}
View.OnClickListener onAuth =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onAuth");
Intent i;
i =new Intent(mContext, AuthenticateActivity.class);
startActivity(i);
}
};
View.OnClickListener onPruneBtn =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onPruneBtn");
mLm.pruneLocalDb();
}
};
/*
* Start the timer that will upload data to the remote server after a given period.
*/
private void startUiTimer() {
if (mUiTimer != null) {
Log.v(TAG, "startRemoteLogTimer -timer already running - cancelling it");
mUiTimer.cancel();
mUiTimer = null;
}
Log.v(TAG, "startRemoteLogTimer() - starting RemoteLogTimer");
mUiTimer =
new UiTimer(1000, 1000);
mUiTimer.start();
}
/*
* Cancel the remote logging timer to prevent attempts to upload to remote database.
*/
public void stopUiTimer() {
if (mUiTimer != null) {
Log.v(TAG, "stopRemoteLogTimer(): cancelling Remote Log timer");
mUiTimer.cancel();
mUiTimer = null;
}
}
/**
* Upload recorded data to the remote database periodically.
*/
private class UiTimer extends CountDownTimer {
public UiTimer(long startTime, long interval) {
super(startTime, interval);
}
@Override
public void onTick(long l) {
// Do Nothing
}
@Override
public void onFinish() {
Log.v(TAG, "UiTimer - onFinish - Updating UI");
updateUi();
// Restart this timer.
start();
}
}
}

View File

@@ -0,0 +1,427 @@
package uk.org.openseizuredetector;
//import androidx.appcompat.app.AppCompatActivity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.TimePicker;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* ReportSeizureActivity - Allows the user to report a seizure manually, which is saved in the database for
* future analysis - particularlly useful if OpenSeizureDetector did not detect the seizure automatically as this
* will ensure the data for the missed seizure is saved.
* Based on: https://www.journaldev.com/9976/android-date-time-picker-dialog
*/
public class ReportSeizureActivity extends AppCompatActivity {
private String TAG = "ReportSeizureActivity";
private Context mContext;
private UiTimer mUiTimer;
private LogManager mLm;
private WebApiConnection mWac;
private int mYear, mMonth, mDay, mHour, mMinute;
private String mMsg = "Messages";
private SdServiceConnection mConnection;
private OsdUtil mUtil;
final Handler serverStatusHandler = new Handler();
private List<String> mEventTypesList = null;
private HashMap<String, ArrayList<String>> mEventSubTypesHashMap = null;
private String mEventTypeStr = null;
private String mEventSubTypeStr = null;
private String mEventNotes = "";
private RadioGroup mEventTypeRg;
private boolean mRedrawEventSubTypesList = false;
private boolean mRedrawEventTypesList = false;
private RadioGroup mEventSubTypeRg;
private boolean mEventSubTypesListChanged = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
mContext = this;
mUtil = new OsdUtil(this, serverStatusHandler);
if (!mUtil.isServerRunning()) {
mUtil.showToast(getString(R.string.error_server_not_running));
finish();
return;
}
mContext = this;
mConnection = new SdServiceConnection(getApplicationContext());
setContentView(R.layout.activity_report_seizure);
mEventTypeRg = findViewById(R.id.eventTypeRg);
mEventTypeRg.setOnCheckedChangeListener(onEventTypeChange);
mEventSubTypeRg = findViewById(R.id.eventSubTypeRg);
mEventSubTypeRg.setOnCheckedChangeListener(onEventSubTypeChange);
Button okBtn =
(Button) findViewById(R.id.loginBtn);
okBtn.setOnClickListener(onOk);
Button cancelBtn =
(Button) findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(onCancel);
Button setDateBtn =
(Button) findViewById(R.id.select_date_button);
setDateBtn.setOnClickListener(onSelectDate);
Button setTimeBtn =
(Button) findViewById(R.id.select_time_button);
setTimeBtn.setOnClickListener(onSelectTime);
// Get Current Date
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
mHour = c.get(Calendar.HOUR_OF_DAY);
mMinute = c.get(Calendar.MINUTE);
}
@Override
protected void onStart() {
super.onStart();
mUtil.bindToServer(getApplicationContext(), mConnection);
waitForConnection();
}
@Override
protected void onPause() {
super.onPause();
stopUiTimer();
}
@Override
protected void onResume() {
super.onResume();
//startUiTimer();
}
@Override
protected void onStop() {
super.onStop();
mUtil.unbindFromServer(getApplicationContext(), mConnection);
}
private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for
// the mConnection to bind to the service, so we delay half a second to give it chance
// to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer)
if (mConnection.mBound) {
Log.v(TAG, "waitForConnection - Bound!");
initialiseServiceConnection();
} else {
Log.v(TAG, "waitForConnection - waiting...");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
waitForConnection();
}
}, 100);
}
}
private void initialiseServiceConnection() {
mLm = mConnection.mSdServer.mLm;
mWac = mConnection.mSdServer.mLm.mWac;
if (mWac.isLoggedIn()) {
// Retrieve the JSONObject containing the standard event types.
// Note this obscure syntax is to avoid having to create another interface, so it is worth it :)
// See https://medium.com/@pra4mesh/callback-function-in-java-20fa48b27797
mWac.getEventTypes(new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventTypesObj) {
Log.v(TAG, "initialiseServiceConnection().onEventTypesReceived");
if (eventTypesObj == null) {
Log.e(TAG, "initialiseServiceConnection().getEventTypes Callback: Error Retrieving event types");
mUtil.showToast("Error Retrieving Event Types from Server - Please Try Again Later!");
} else {
Iterator<String> keys = eventTypesObj.keys();
mEventTypesList = new ArrayList<String>();
mEventSubTypesHashMap = new HashMap<String, ArrayList<String>>();
while (keys.hasNext()) {
String key = keys.next();
Log.v(TAG, "initialiseServiceConnection().getEventTypes Callback: key=" + key);
mEventTypesList.add(key);
try {
JSONArray eventSubTypes = eventTypesObj.getJSONArray(key);
ArrayList<String> eventSubtypesList = new ArrayList<String>();
for (int i = 0; i < eventSubTypes.length(); i++) {
eventSubtypesList.add(eventSubTypes.getString(i));
}
mEventSubTypesHashMap.put(key, eventSubtypesList);
mRedrawEventSubTypesList = true;
} catch (JSONException e) {
Log.e(TAG, "initialiseServiceConnection().getEventTypes Callback: Error parsing JSONObject" + e.getMessage() + e.toString());
}
}
mRedrawEventTypesList = true;
updateUi();
}
}
});
} else {
new AlertDialog.Builder(mContext)
.setTitle(R.string.not_logged_in_dialog_title)
.setMessage(R.string.not_logged_in_dialog_message)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
finish();
}
})
.show();
}
}
private void updateUi() {
//Log.v(TAG,"updateUi()");
TextView tv;
Button btn;
RadioButton b;
tv = (TextView)findViewById(R.id.date_day_tv);
tv.setText(String.format("%02d",mDay));
tv = (TextView)findViewById(R.id.date_mon_tv);
tv.setText(String.format("%02d",mMonth+1)); // Month counted from zero
tv = (TextView)findViewById(R.id.date_year_tv);
tv.setText(String.format("%04d",mYear));
tv = (TextView)findViewById(R.id.time_hh_tv);
tv.setText(String.format("%02d",mHour));
tv = (TextView)findViewById(R.id.time_mm_tv);
tv.setText(String.format("%02d",mMinute));
tv = (TextView)findViewById(R.id.msg_tv);
tv.setText(mMsg);
// Populate event type button group if necessary
if (mEventTypesList != null && mRedrawEventTypesList) {
Log.v(TAG, "updateUi: " + mEventTypesList.toString());
mEventTypeRg.removeAllViews();
for (String eventTypeStr : mEventTypesList) {
b = new RadioButton(this);
b.setText(eventTypeStr);
mEventTypeRg.addView(b);
}
mRedrawEventTypesList = false;
}
String seizureTypeStr = null;
// Find which seizure type is selected
int checkedRadioButtonId = mEventTypeRg.getCheckedRadioButtonId();
//Log.i(TAG,"updateUi(): checkedRadioButtonId="+checkedRadioButtonId);
b = (RadioButton) findViewById(checkedRadioButtonId);
if (b != null) {
seizureTypeStr = b.getText().toString();
}
Log.i(TAG,"updateUi - SeizureType="+seizureTypeStr);
// Populate the event sub-types radio button list.
Log.v(TAG,"updateUi() - meventsubtypeshashmap="+mEventSubTypesHashMap+", mEventSubtypesListChanged="+mEventSubTypesListChanged);
if (mEventSubTypesHashMap != null && mRedrawEventSubTypesList) {
Log.v(TAG,"UpdateUi() - populating event sub types list");
if (seizureTypeStr != null) {
// based on https://androidexample.com/create-a-simple-listview
ArrayList<String> subtypesArrayList = mEventSubTypesHashMap.get(seizureTypeStr);
Log.v(TAG, "updateUi() - eventType=" + seizureTypeStr + ", subtypes=" + subtypesArrayList);
mEventSubTypeRg.removeAllViews();
for (String eventSubTypeStr : subtypesArrayList) {
b = new RadioButton(this);
b.setText(eventSubTypeStr);
mEventSubTypeRg.addView(b);
}
mRedrawEventSubTypesList = false;
}
}
}
View.OnClickListener onOk =
new View.OnClickListener() {
@Override
public void onClick(View view) {
RadioButton b;
String seizureTypeStr = null;
String seizureSubTypeStr = null;
String notesStr = null;
Log.v(TAG, "onOk");
//SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr=String.format("%4d-%02d-%02d %02d:%02d:30",mYear,mMonth+1,mDay, mHour, mMinute);
Log.v(TAG, "onOk() - dateSTr="+dateStr);
// Read seizure type from radio buttons
int checkedRadioButtonId = mEventTypeRg.getCheckedRadioButtonId();
b = (RadioButton) findViewById(checkedRadioButtonId);
if (b != null) {
seizureTypeStr = b.getText().toString();
}
Log.i(TAG,"onOk() - SeizureType="+seizureTypeStr);
checkedRadioButtonId = mEventSubTypeRg.getCheckedRadioButtonId();
b = (RadioButton) findViewById(checkedRadioButtonId);
if (b != null) {
seizureSubTypeStr = b.getText().toString();
}
Log.i(TAG,"onOk() - SeizureSubType="+seizureSubTypeStr);
TextView tv = (TextView)findViewById(R.id.eventNotesTv);
notesStr = tv.getText().toString();
mLm.createLocalEvent(dateStr,5,seizureTypeStr, seizureSubTypeStr, notesStr,
mConnection.mSdServer.mSdData.toSettingsJSON());
mUtil.showToast("Seizure Event Created");
finish();
}
};
View.OnClickListener onCancel =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onCancel");
finish();
}
};
View.OnClickListener onSelectDate =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onSelectDate()");
DatePickerDialog datePickerDialog = new DatePickerDialog(mContext,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
mYear = year;
mMonth = monthOfYear;
mDay = dayOfMonth;
}
}, mYear, mMonth, mDay);
datePickerDialog.show();
}
};
View.OnClickListener onSelectTime =
new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.v(TAG, "onSelectTime()");
TimePickerDialog timePickerDialog = new TimePickerDialog(mContext,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay,
int minute) {
mHour = hourOfDay;
mMinute = minute;
}
}, mHour, mMinute, true);
timePickerDialog.show();
}
};
RadioGroup.OnCheckedChangeListener onEventTypeChange =
new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
mRedrawEventSubTypesList = true;
updateUi();
}
};
RadioGroup.OnCheckedChangeListener onEventSubTypeChange =
new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
updateUi();
}
};
/*
* Start the timer that will upload data to the remote server after a given period.
*/
private void startUiTimer() {
if (mUiTimer != null) {
Log.v(TAG, "startUiTimer -timer already running - cancelling it");
mUiTimer.cancel();
mUiTimer = null;
}
Log.v(TAG, "startUiTimer() - starting UiTimer");
mUiTimer =
new UiTimer(1000, 1000);
mUiTimer.start();
}
/*
* Cancel the remote logging timer to prevent attempts to upload to remote database.
*/
public void stopUiTimer() {
if (mUiTimer != null) {
Log.v(TAG, "stopUiTimer(): cancelling Ui timer");
mUiTimer.cancel();
mUiTimer = null;
}
}
/**
* Update User Interface Periodically
*/
private class UiTimer extends CountDownTimer {
public UiTimer(long startTime, long interval) {
super(startTime, interval);
}
@Override
public void onTick(long l) {
// Do Nothing
}
@Override
public void onFinish() {
//Log.v(TAG, "UiTimer - onFinish - Updating UI");
updateUi();
// Restart this timer.
start();
}
}
}

View File

@@ -38,6 +38,7 @@ public class SdData implements Parcelable {
private final static String TAG = "SdData";
private final static int N_RAW_DATA = 500; // 5 seconds at 100 Hz.
/* Analysis settings */
public String phoneAppVersion = "";
public boolean haveSettings = false; // flag to say if we have received settings or not.
public boolean haveData = false; // flag to say we have received data.
public short mDataUpdatePeriod;
@@ -65,7 +66,22 @@ public class SdData implements Parcelable {
public boolean mHRNullAsAlarm = false;
public double mHRThreshMin = 40.0;
public double mHRThreshMax = 150.0;
/* Oxygen Saturation Alarm Settings */
public boolean mO2SatAlarmActive = false;
public boolean mO2SatNullAsAlarm = false;
public double mO2SatThreshMin = 80.0;
/* Watch App Settings */
public String dataSourceName = "";
public String watchPartNo = "";
public String watchFwVersion = "";
public String watchSdVersion = "";
public String watchSdName = "";
public double rawData[];
public double rawData3D[];
int mNsamp = 0;
/* Analysis results */
@@ -87,14 +103,21 @@ public class SdData implements Parcelable {
public boolean mHRFaultStanding = false;
public double mHR = 0;
public boolean mO2SatAlarmStanding = false;
public boolean mO2SatFaultStanding = false;
public double mO2Sat = 0;
public SdData() {
simpleSpec = new int[10];
rawData = new double[N_RAW_DATA];
rawData3D = new double[N_RAW_DATA * 3];
dataTime = new Time(Time.getCurrentTimezone());
}
/*
* Intialise this SdData object from a JSON String
* FIXME - add O2saturation with checking in case it is not included in the data
*/
public boolean fromJSON(String jsonStr) {
Log.v(TAG, "fromJSON() - parsing jsonString - " + jsonStr);
@@ -120,12 +143,12 @@ public class SdData implements Parcelable {
alarmPhrase = jo.optString("alarmPhrase");
alarmThresh = jo.optInt("alarmThresh");
alarmRatioThresh = jo.optInt("alarmRatioThresh");
mHRAlarmActive=jo.optBoolean("hrAlarmActive");
mHRAlarmActive = jo.optBoolean("hrAlarmActive");
mHRAlarmStanding = jo.optBoolean("hrAlarmStanding");
mHRThreshMin = jo.optDouble("hrThreshMin");
mHRThreshMax = jo.optDouble("hrThreshMax");
mHR = jo.optDouble("hr");
if (mHR>=0.0) {
if (mHR >= 0.0) {
mHRAlarmActive = true;
}
JSONArray specArr = jo.optJSONArray("simpleSpec");
@@ -136,7 +159,7 @@ public class SdData implements Parcelable {
Log.v(TAG, "fromJSON(): sdData = " + this.toString());
return true;
} catch (Exception e) {
Log.v(TAG, "fromJSON() - error parsing result"+e.toString());
Log.v(TAG, "fromJSON() - error parsing result" + e.toString());
haveData = false;
return false;
}
@@ -147,6 +170,106 @@ public class SdData implements Parcelable {
return toDataString(false);
}
public String toJSON(boolean includeRawData) {
return toDataString(includeRawData);
}
public String toDatapointJSON() {
String retval;
retval = "SdData.toDatapointJSON() Output";
try {
JSONObject jsonObj = new JSONObject();
if (dataTime != null) {
jsonObj.put("dataTime", dataTime.format("%d-%m-%Y %H:%M:%S"));
jsonObj.put("dataTimeStr", dataTime.format("%Y%m%dT%H%M%S"));
} else {
jsonObj.put("dataTimeStr", "00000000T000000");
jsonObj.put("dataTime", "00-00-00 00:00:00");
}
Log.v(TAG, "mSdData.dataTime = " + dataTime);
jsonObj.put("maxVal", maxVal);
jsonObj.put("maxFreq", maxFreq);
jsonObj.put("specPower", specPower);
jsonObj.put("roiPower", roiPower);
jsonObj.put("roiRatio", 10 * roiPower / specPower);
jsonObj.put("alarmState", alarmState);
jsonObj.put("alarmPhrase", alarmPhrase);
jsonObj.put("hr", mHR);
jsonObj.put("o2Sat", mO2Sat);
JSONArray arr = new JSONArray();
for (int i = 0; i < simpleSpec.length; i++) {
arr.put(simpleSpec[i]);
}
jsonObj.put("simpleSpec", arr);
JSONArray rawArr = new JSONArray();
for (int i = 0; i < rawData.length; i++) {
rawArr.put(rawData[i]);
}
//Log.v(TAG,"rawData[0]="+rawData[0]+", rawArr[0]="+rawArr.getDouble(0));
jsonObj.put("rawData", rawArr);
JSONArray raw3DArr = new JSONArray();
for (int i = 0; i < rawData3D.length; i++) {
raw3DArr.put(rawData3D[i]);
}
jsonObj.put("rawData3D", raw3DArr);
retval = jsonObj.toString();
Log.v(TAG,"retval rawData="+retval);
} catch (Exception ex) {
Log.v(TAG, "Error Creating Data Object - " + ex.toString());
retval = "Error Creating Data Object - " + ex.toString();
}
return (retval);
}
public String toSettingsJSON() {
String retval;
retval = "SdData.toSettingsJSON() Output";
try {
JSONObject jsonObj = new JSONObject();
if (dataTime != null) {
jsonObj.put("dataTime", dataTime.format("%d-%m-%Y %H:%M:%S"));
jsonObj.put("dataTimeStr", dataTime.format("%Y%m%dT%H%M%S"));
} else {
jsonObj.put("dataTimeStr", "00000000T000000");
jsonObj.put("dataTime", "00-00-00 00:00:00");
}
jsonObj.put("batteryPc", batteryPc);
jsonObj.put("alarmState", alarmState);
jsonObj.put("alarmPhrase", alarmPhrase);
jsonObj.put("sdMode", mSdMode);
jsonObj.put("sampleFreq", mSampleFreq);
jsonObj.put("analysisPeriod", analysisPeriod);
jsonObj.put("alarmFreqMin", alarmFreqMin);
jsonObj.put("alarmFreqMax", alarmFreqMax);
jsonObj.put("alarmThresh", alarmThresh);
jsonObj.put("alarmRatioThresh", alarmRatioThresh);
jsonObj.put("hrAlarmActive", mHRAlarmActive);
jsonObj.put("hrAlarmStanding", mHRAlarmStanding);
jsonObj.put("hrThreshMin", mHRThreshMin);
jsonObj.put("hrThreshMax", mHRThreshMax);
jsonObj.put("o2SatAlarmActive", mO2SatAlarmActive);
jsonObj.put("o2SatAlarmStanding", mO2SatAlarmStanding);
jsonObj.put("o2SatThreshMin", mO2SatThreshMin);
jsonObj.put("dataSourceName", dataSourceName);
Log.v(TAG,"phoneAppVersion="+phoneAppVersion);
jsonObj.put("phoneAppVersion", phoneAppVersion);
jsonObj.put("watchPartNo", watchPartNo);
jsonObj.put("watchSdName", watchSdName);
jsonObj.put("watchFwVersion", watchFwVersion);
jsonObj.put("watchSdVersion", watchSdVersion);
retval = jsonObj.toString();
} catch (Exception ex) {
Log.e(TAG, "toSettingsJSON(): Error Creating Data Object - " + ex.toString());
retval = "Error Creating Data Object - " + ex.toString();
}
return (retval);
}
public String toDataString(boolean includeRawData) {
String retval;
retval = "SdData.toDataString() Output";
@@ -155,7 +278,7 @@ public class SdData implements Parcelable {
if (dataTime != null) {
jsonObj.put("dataTime", dataTime.format("%d-%m-%Y %H:%M:%S"));
jsonObj.put("dataTimeStr", dataTime.format("%Y%m%dT%H%M%S"));
}else{
} else {
jsonObj.put("dataTimeStr", "00000000T000000");
jsonObj.put("dataTime", "00-00-00 00:00:00");
}
@@ -170,18 +293,22 @@ public class SdData implements Parcelable {
jsonObj.put("haveSettings", haveSettings);
jsonObj.put("alarmState", alarmState);
jsonObj.put("alarmPhrase", alarmPhrase);
jsonObj.put("sdMode",mSdMode);
jsonObj.put("sampleFreq",mSampleFreq);
jsonObj.put("analysisPeriod",analysisPeriod);
jsonObj.put("alarmFreqMin",alarmFreqMin);
jsonObj.put("alarmFreqMax",alarmFreqMax);
jsonObj.put("sdMode", mSdMode);
jsonObj.put("sampleFreq", mSampleFreq);
jsonObj.put("analysisPeriod", analysisPeriod);
jsonObj.put("alarmFreqMin", alarmFreqMin);
jsonObj.put("alarmFreqMax", alarmFreqMax);
jsonObj.put("alarmThresh", alarmThresh);
jsonObj.put("alarmRatioThresh", alarmRatioThresh);
jsonObj.put("hrAlarmActive", mHRAlarmActive);
jsonObj.put("hrAlarmStanding", mHRAlarmStanding);
jsonObj.put("hrThreshMin",mHRThreshMin);
jsonObj.put("hrThreshMin", mHRThreshMin);
jsonObj.put("hrThreshMax", mHRThreshMax);
jsonObj.put("hr",mHR);
jsonObj.put("hr", mHR);
jsonObj.put("o2SatAlarmActive", mO2SatAlarmActive);
jsonObj.put("o2SatAlarmStanding", mO2SatAlarmStanding);
jsonObj.put("o2SatThreshMin", mO2SatThreshMin);
jsonObj.put("o2Sat", mO2Sat);
JSONArray arr = new JSONArray();
for (int i = 0; i < simpleSpec.length; i++) {
arr.put(simpleSpec[i]);
@@ -189,9 +316,17 @@ public class SdData implements Parcelable {
jsonObj.put("simpleSpec", arr);
if (includeRawData) {
JSONArray rawArr = new JSONArray();
for (int i = 0; i< rawData.length;i++) {
for (int i = 0; i < rawData.length; i++) {
rawArr.put(rawData[i]);
}
jsonObj.put("rawData", rawArr);
JSONArray raw3DArr = new JSONArray();
for (int i = 0; i < rawData3D.length; i++) {
raw3DArr.put(rawData3D[i]);
}
jsonObj.put("rawData3D", raw3DArr);
}
retval = jsonObj.toString();
@@ -209,7 +344,7 @@ public class SdData implements Parcelable {
retval = "";
if (dataTime != null) {
retval = dataTime.format("%d-%m-%Y %H:%M:%S");
}else{
} else {
retval = "00-00-00 00:00:00";
}
for (int i = 0; i < simpleSpec.length; i++) {
@@ -220,32 +355,37 @@ public class SdData implements Parcelable {
retval = retval + ", " + mSampleFreq;
retval = retval + ", " + alarmPhrase;
retval = retval + ", " + mHR;
retval = retval + ", " + mO2Sat;
if (includeRawData) {
for (int i = 0; i< mNsamp;i++) {
for (int i = 0; i < mNsamp; i++) {
retval = retval + ", " + rawData[i];
}
}
return(retval);
return (retval);
}
/** Return the average acceleration value in the dataset */
/**
* Return the average acceleration value in the dataset
*/
public double getAvAcc() {
double sumAcc = 0.0;
for (int i = 0; i< mNsamp;i++) {
for (int i = 0; i < mNsamp; i++) {
sumAcc += rawData[i];
}
return(sumAcc/mNsamp);
return (sumAcc / mNsamp);
}
/** Return the standard deviation of the acceleration values */
/**
* Return the standard deviation of the acceleration values
*/
public double getSdAcc() {
double avAcc = 0.0;
double varAcc = 0.0;
avAcc = getAvAcc();
for (int i = 0; i< mNsamp;i++) {
varAcc += Math.pow(rawData[i]-avAcc,2);
for (int i = 0; i < mNsamp; i++) {
varAcc += Math.pow(rawData[i] - avAcc, 2);
}
return(Math.sqrt(varAcc/(mNsamp-1)));
return (Math.sqrt(varAcc / (mNsamp - 1)));
}

View File

@@ -254,6 +254,7 @@ public abstract class SdDataSource {
String sdVersion;
String sdName;
JSONArray accelVals = null;
JSONArray accelVals3D = null;
Log.v(TAG, "updateFromJSON - " + jsonStr);
try {
@@ -270,6 +271,12 @@ public abstract class SdDataSource {
// if we get 'null' HR (For example if the heart rate is not working)
mSdData.mHR = -1;
}
try {
mSdData.mO2Sat = dataObject.getDouble("O2sat");
} catch (JSONException e) {
// if we get 'null' O2 Saturation (For example if the oxygen sensor is not working)
mSdData.mO2Sat = -1;
}
try {
mMute = dataObject.getInt("Mute");
} catch (JSONException e) {
@@ -284,12 +291,31 @@ public abstract class SdDataSource {
}
int i;
for (i = 0; i < accelVals.length(); i++) {
mSdData.rawData[i] = accelVals.getInt(i);
mSdData.rawData[i] = accelVals.getDouble(i);
}
mSdData.mNsamp = accelVals.length();
//mNSamp = accelVals.length();
//Log.d(TAG,"accelVals[0]="+accelVals.getDouble(0)+", mSdData.rawData[0]="+mSdData.rawData[0]);
try {
accelVals3D = dataObject.getJSONArray("data3D");
Log.v(TAG, "Received " + accelVals3D.length() + " acceleration 3D values, rawData Length is " + mSdData.rawData3D.length);
if (accelVals3D.length() > mSdData.rawData3D.length) {
mUtil.writeToSysLogFile("ERROR: Received " + accelVals3D.length() + " 3D acceleration values, but rawData3D storage length is "
+ mSdData.rawData3D.length);
}
for (i = 0; i < accelVals3D.length(); i++) {
mSdData.rawData3D[i] = accelVals3D.getDouble(i);
}
} catch (JSONException e) {
// If we get an error, just set rawData3D to zero
Log.i(TAG,"updateFromJSON - error parsing 3D data - setting it to zero");
for (i = 0; i < mSdData.rawData3D.length; i++) {
mSdData.rawData3D[i] = 0.;
}
}
mWatchAppRunningCheck = true;
doAnalysis();
if (mSdData.haveSettings == false) {
retVal = "sendSettings";
} else {
@@ -312,6 +338,10 @@ public abstract class SdDataSource {
sdName = dataObject.getString("sdName");
mUtil.writeToSysLogFile(" * sdName = " + sdName + " version " + sdVersion);
mUtil.writeToSysLogFile(" * watchPartNo = " + watchPartNo + " fwVersion " + watchFwVersion);
mSdData.watchPartNo = watchPartNo;
mSdData.watchFwVersion = watchFwVersion;
mSdData.watchSdVersion = sdVersion;
mSdData.watchSdName = sdName;
} catch (Exception e) {
Log.e(TAG, "updateFromJSON - Error Parsing V3.2 JSON String - " + e.toString());
mUtil.writeToSysLogFile("updateFromJSON - Error Parsing V3.2 JSON String - " + jsonStr + " - " + e.toString());
@@ -455,8 +485,10 @@ public abstract class SdDataSource {
// Check this data to see if it represents an alarm state.
alarmCheck();
hrCheck();
o2SatCheck();
fallCheck();
muteCheck();
Log.v(TAG,"after fallCheck, mSdData.fallAlarmStanding="+mSdData.fallAlarmStanding);
mSdDataReceiver.onSdDataReceived(mSdData); // and tell SdServer we have received data.
}
@@ -470,9 +502,12 @@ public abstract class SdDataSource {
*/
private void alarmCheck() {
boolean inAlarm;
Log.v(TAG, "alarmCheck()");
// Avoid potential divide by zero issue
if (mSdData.specPower == 0)
mSdData.specPower = 1;
Log.v(TAG, "alarmCheck() - roiPower="+mSdData.roiPower+" specPower="+ mSdData.specPower+" ratio="+10*mSdData.roiPower/ mSdData.specPower);
// Is the current set of data representing an alarm state?
if ((mSdData.roiPower > mAlarmThresh) && (10 * (mSdData.roiPower / mSdData.specPower) > mAlarmRatioThresh)) {
if ((mSdData.roiPower > mAlarmThresh) && ((10 * mSdData.roiPower / mSdData.specPower) > mAlarmRatioThresh)) {
inAlarm = true;
} else {
inAlarm = false;
@@ -502,7 +537,7 @@ public abstract class SdDataSource {
}
}
Log.v(TAG, "alarmCheck(): inAlarm=" + inAlarm + ", alarmState = " + mSdData.alarmState + " alarmCount=" + mAlarmCount + " mAlarmTime=" + mAlarmTime);
Log.v(TAG, "alarmCheck(): inAlarm=" + inAlarm + ", alarmState = " + mSdData.alarmState + " alarmCount=" + mAlarmCount + " mWarnTime=" + mWarnTime+ " mAlarmTime=" + mAlarmTime);
}
@@ -543,9 +578,39 @@ public abstract class SdDataSource {
mSdData.mHRAlarmStanding = false;
}
}
}
/**
* hrCheck - check the Heart rate data in mSdData to see if it represents an alarm condition.
* Sets mSdData.mHRAlarmStanding
*/
public void o2SatCheck() {
Log.v(TAG, "o2SatCheck()");
/* Check Oxygen Saturation against alarm settings */
if (mSdData.mO2SatAlarmActive) {
if (mSdData.mO2Sat < 0) {
if (mSdData.mO2SatNullAsAlarm) {
Log.i(TAG, "Oxygen Saturation Null - Alarming");
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = true;
} else {
Log.i(TAG, "Oxygen Saturation Fault (O2Sat<0)");
mSdData.mO2SatFaultStanding = true;
mSdData.mO2SatAlarmStanding = false;
}
} else if (mSdData.mO2Sat < mSdData.mO2SatThreshMin) {
Log.i(TAG, "Oxygen Saturation Abnormal - " + mSdData.mO2Sat + " %");
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = true;
} else {
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = false;
}
}
}
/****************************************************************
* Simple threshold analysis to chech for fall.
* Called from clock_tick_handler()
@@ -570,8 +635,9 @@ public abstract class SdDataSource {
if (mSdData.rawData[i + j] < minAcc) minAcc = mSdData.rawData[i + j];
if (mSdData.rawData[i + j] > maxAcc) maxAcc = mSdData.rawData[i + j];
}
Log.d(TAG, "check_fall() - minAcc=" + minAcc +" (mFallThreshMin="+mFallThreshMin+ "), maxAcc=" + maxAcc+" (mFallThreshMax="+mFallThreshMax+")") ;
if ((minAcc < mFallThreshMin) && (maxAcc > mFallThreshMax)) {
Log.d(TAG, "check_fall() - minAcc=" + minAcc + ", maxAcc=" + maxAcc);
Log.d(TAG, "check_fall() ****FALL DETECTED***** minAcc=" + minAcc + ", maxAcc=" + maxAcc);
Log.d(TAG, "check_fall() - ****FALL DETECTED****");
mSdData.fallAlarmStanding = true;
return;
@@ -649,11 +715,11 @@ public abstract class SdDataSource {
// get time since the last data was received from the watch.
tdiff = (tnow.toMillis(false) - mDataStatusTime.toMillis(false));
Log.v(TAG, "faultCheck() - tdiff=" + tdiff + ", mDataUpatePeriod=" + mDataUpdatePeriod + ", mAppRestartTimeout=" + mAppRestartTimeout
+ ", combined = " + (mDataUpdatePeriod + mAppRestartTimeout) * 1000);
//Log.v(TAG, "faultCheck() - tdiff=" + tdiff + ", mDataUpatePeriod=" + mDataUpdatePeriod + ", mAppRestartTimeout=" + mAppRestartTimeout
// + ", combined = " + (mDataUpdatePeriod + mAppRestartTimeout) * 1000);
if (!mWatchAppRunningCheck &&
(tdiff > (mDataUpdatePeriod + mAppRestartTimeout) * 1000)) {
Log.v(TAG, "faultCheck() - watch app not running so not doing anything");
//Log.v(TAG, "faultCheck() - watch app not running so not doing anything");
mAlarmCount = 0;
}
}
@@ -814,6 +880,19 @@ public abstract class SdDataSource {
Log.v(TAG, "updatePrefs() HRThreshMax = " + mSdData.mHRThreshMax);
mUtil.writeToSysLogFile( "updatePrefs() HRThreshMax = " + mSdData.mHRThreshMax);
mSdData.mO2SatAlarmActive = SP.getBoolean("O2SatAlarmActive", false);
Log.v(TAG, "updatePrefs() O2SatAlarmActive = " + mSdData.mO2SatAlarmActive);
mUtil.writeToSysLogFile( "updatePrefs() O2SatAlarmActive = " + mSdData.mO2SatAlarmActive);
mSdData.mO2SatNullAsAlarm = SP.getBoolean("O2SatNullAsAlarm", false);
Log.v(TAG, "updatePrefs() O2SatNullAsAlarm = " + mSdData.mO2SatNullAsAlarm);
mUtil.writeToSysLogFile( "updatePrefs() O2SatNullAsAlarm = " + mSdData.mO2SatNullAsAlarm);
prefStr = SP.getString("O2SatThreshMin", "SET_FROM_XML");
mSdData.mO2SatThreshMin = (short) Integer.parseInt(prefStr);
Log.v(TAG, "updatePrefs() O2SatThreshMin = " + mSdData.mO2SatThreshMin);
mUtil.writeToSysLogFile( "updatePrefs() O2SatThreshMin = " + mSdData.mO2SatThreshMin);
} else {
Log.v(TAG, "updatePrefs() - prefStr is null - WHY????");
mUtil.writeToSysLogFile("SDDataSource.updatePrefs() - prefStr is null - WHY??");

File diff suppressed because one or more lines are too long

View File

@@ -63,6 +63,7 @@ public class SdDataSourceBLE extends SdDataSource {
private int nRawData = 0;
private double[] rawData = new double[MAX_RAW_DATA];
private boolean waitForDescriptorWrite = false;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
@@ -82,13 +83,12 @@ public class SdDataSourceBLE extends SdDataSource {
public static String SERV_DEV_INFO = "0000180a-0000-1000-8000-00805f9b34fb";
public static String SERV_HEART_RATE = "0000180d-0000-1000-8000-00805f9b34fb";
public static String SERV_OSD = "a19585e9-0001-39d0-015f-b3e2b9a0c854";
public static String SERV_OSD = "000085e9-0000-1000-8000-00805f9b34fb";
public static String CHAR_HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
public static String CHAR_MANUF_NAME = "00002a29-0000-1000-8000-00805f9b34fb";
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
public static String CHAR_OSD_ACC_DATA = "a19585e9-0002-39d0-015f-b3e2b9a0c854";
public static String CHAR_OSD_BATT_DATA = "a19585e9-0004-39d0-015f-b3e2b9a0c854";
public static String CHAR_OSD_ACC_DATA = "000085ea-0000-1000-8000-00805f9b34fb";
public static String CHAR_OSD_BATT_DATA = "000085eb-0000-1000-8000-00805f9b34fb";
public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(CHAR_HEART_RATE_MEASUREMENT);
private BluetoothGatt mGatt;
@@ -264,7 +264,8 @@ public class SdDataSourceBLE extends SdDataSource {
}
else if (charUuidStr.equals(CHAR_OSD_BATT_DATA)) {
Log.v(TAG,"Saving battery characteristic for later");
mBattChar = gattCharacteristic;
Log.v(TAG, "Subscribing to battery change Notifications");
setCharacteristicNotification(gattCharacteristic,true);
}
}
}
@@ -287,6 +288,8 @@ public class SdDataSourceBLE extends SdDataSource {
}
public void onDataReceived(BluetoothGattCharacteristic characteristic) {
Log.v(TAG, "onDataReceived uuid" + characteristic.getUuid().toString());
// FIXME - collect data until we have enough to do analysis, then use onDataReceived to process it.
//Log.v(TAG,"onDataReceived: Characteristic="+characteristic.getUuid().toString());
if (characteristic.getUuid().toString().equals(CHAR_HEART_RATE_MEASUREMENT)) {
@@ -294,12 +297,13 @@ public class SdDataSourceBLE extends SdDataSource {
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
//Log.d(TAG, "Heart rate format UINT16.");
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
//Log.d(TAG, "Heart rate format UINT8.");
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
mSdData.mHR = (double) heartRate;
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
}
else if (characteristic.getUuid().toString().equals(CHAR_OSD_ACC_DATA)) {
@@ -322,21 +326,15 @@ public class SdDataSourceBLE extends SdDataSource {
//mNSamp = accelVals.length();
mWatchAppRunningCheck = true;
mDataStatusTime = new Time(Time.getCurrentTimezone());
if (mSdData.haveSettings == false) {
Log.v(TAG,"Requesting Battery Data");
mGatt.readCharacteristic(mBattChar);
}
doAnalysis();
nRawData = 0;
}
}
}
else if (characteristic.getUuid().toString().equals(CHAR_OSD_BATT_DATA)) {
mSdData.batteryPc = characteristic.getValue()[0];
Log.v(TAG,"Received Battery Data");
byte batteryPc = characteristic.getValue()[0];
mSdData.batteryPc = batteryPc;
Log.v(TAG,"Received Battery Data" + String.format("%d", batteryPc));
mSdData.haveSettings = true;
}
else {
@@ -361,8 +359,13 @@ public class SdDataSourceBLE extends SdDataSource {
Log.v(TAG,"onCharacteristicChanged(): Characteristic "+characteristic.getUuid()+" changed");
onDataReceived(characteristic);
}
};
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
Log.v(TAG,"onDescriptorWrite(): Characteristic " + descriptor.getUuid() + " changed");
waitForDescriptorWrite = false;
}
};
/**
* Enables or disables notification on a give characteristic.
@@ -370,12 +373,27 @@ public class SdDataSourceBLE extends SdDataSource {
* @param characteristic Characteristic to act on.
* @param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
public void setCharacteristicNotification(final BluetoothGattCharacteristic characteristic, final boolean enabled) {
Log.w(TAG, "setCharacteristicNotification " + characteristic.getUuid());
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
if (waitForDescriptorWrite) {
// Apparently if you try to write multiple descriptors too quickly then only
// one is processed, hence why this waiting logic is necessary
Log.w(TAG, "waitForDescriptor " + characteristic.getUuid());
mHandler.postDelayed(new Runnable() {
public void run() {
Log.w(TAG, "delayed");
setCharacteristicNotification(characteristic, enabled);
}
}, 500);
return;
}
if (enabled) {
Log.v(TAG, "setCharacteristicNotification - Requesting notifications");
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
@@ -396,8 +414,9 @@ public class SdDataSourceBLE extends SdDataSource {
UUID.fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
waitForDescriptorWrite = true;
}
/**
@@ -414,12 +433,3 @@ public class SdDataSourceBLE extends SdDataSource {
}

View File

@@ -129,6 +129,9 @@ public class SdDataSourcePhone extends SdDataSource implements SensorEventListen
float z = event.values[2];
//Log.v(TAG,"Accelerometer Data Received: x="+x+", y="+y+", z="+z);
mSdData.rawData[mSdData.mNsamp] = sqrt(x*x + y*y + z*z);
mSdData.rawData3D[3*mSdData.mNsamp] = x;
mSdData.rawData3D[3*mSdData.mNsamp+1] = y;
mSdData.rawData3D[3*mSdData.mNsamp+2] = z;
mSdData.mNsamp++;
if (mSdData.mNsamp==NSAMP) {
// Calculate the sample frequency for this sample, but do not change mSampleFreq, which is used for
@@ -142,6 +145,9 @@ public class SdDataSourcePhone extends SdDataSource implements SensorEventListen
// FIXME - we should really do this properly rather than assume we are really receiving data at 50Hz.
for (int i=0; i<mSdData.mNsamp; i++) {
mSdData.rawData[i/2] = 1000.*mSdData.rawData[i]/9.81;
mSdData.rawData3D[i/2] = 1000.*mSdData.rawData3D[i]/9.81;
mSdData.rawData3D[i/2 +1] = 1000.*mSdData.rawData3D[i+1]/9.81;
mSdData.rawData3D[i/2 +2] = 1000.*mSdData.rawData3D[i+2]/9.81;
//Log.v(TAG,"i="+i+", rawData="+mSdData.rawData[i]+","+mSdData.rawData[i/2]);
}
mSdData.mNsamp /= 2;

File diff suppressed because it is too large Load Diff

View File

@@ -104,13 +104,18 @@ public class SdWebServer extends NanoHTTPD {
Log.v(TAG, " files=" + files.toString());
String postData = files.get("postData");
Log.v(TAG, " postData=" + postData);
// Send the data to the SdDataSource so the app can pick it up.
if (parameters.get("dataObj") != null) {
Log.v(TAG,"passing parameters to data source");
answer = mSdServer.mSdDataSource.updateFromJSON(parameters.get("dataObj").toString());
if (mSdServer.mSdDataSourceName.equals("Garmin")) {
// Send the data to the SdDataSource so the app can pick it up.
if (parameters.get("dataObj") != null) {
Log.v(TAG, "passing parameters to data source");
answer = mSdServer.mSdDataSource.updateFromJSON(parameters.get("dataObj").toString());
} else {
Log.v(TAG, "Passing postData to data source");
answer = mSdServer.mSdDataSource.updateFromJSON(files.get("postData"));
}
} else {
Log.v(TAG,"Passing postData to data source");
answer = mSdServer.mSdDataSource.updateFromJSON(files.get("postData"));
Log.i(TAG,"Web server received data, but datasource is not set to 'Garmin' - Ignoring");
mUtil.showToast("Web server received data, but datasource is not set to 'Garmin' - Ignoring");
}
break;
default:
@@ -202,7 +207,7 @@ public class SdWebServer extends NanoHTTPD {
} else {
Log.v(TAG, "WebServer.serve() - Unknown uri -" +
uri);
answer = "{'msg' : 'Unknown URI: '}";
answer = "{'msg' : 'Unknown URI: "+uri+"'}";
}
}
res = new NanoHTTPD.Response(answer);

View File

@@ -24,20 +24,22 @@
*/
package uk.org.openseizuredetector;
import android.app.Activity;
import android.app.AlertDialog;
import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.text.Html;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.util.Linkify;
import android.util.Log;
import android.view.View;
@@ -46,6 +48,12 @@ import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.text.HtmlCompat;
import com.rohitss.uceh.UCEHandler;
import java.util.Timer;
@@ -56,7 +64,7 @@ import java.util.TimerTask;
* for it to start and to receive data and settings from the seizure detector before exiting and
* starting the main activity.
*/
public class StartupActivity extends Activity {
public class StartupActivity extends AppCompatActivity {
private static String TAG = "StartupActivity";
private int okColour = Color.BLUE;
private int warnColour = Color.MAGENTA;
@@ -74,11 +82,42 @@ public class StartupActivity extends Activity {
private Handler mHandler = new Handler(); // used to update ui from mUiTimer
private boolean mUsingPebbleDataSource = true;
private String mPebbleAppPackageName = null;
private boolean mBatteryOptDialogDisplayed = false;
private AlertDialog mBatteryOptDialog;
private boolean mLocationPermissions1Requested;
private boolean mLocationPermissions2Requested;
private boolean mSmsPermissionsRequested;
private boolean mPermissionsRequested;
public final String[] REQUIRED_PERMISSIONS = {
//Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.WAKE_LOCK,
};
public final String[] SMS_PERMISSIONS_1 = {
Manifest.permission.SEND_SMS,
Manifest.permission.READ_PHONE_STATE,
};
public final String[] LOCATION_PERMISSIONS_1 = {
Manifest.permission.SEND_SMS,
Manifest.permission.ACCESS_FINE_LOCATION,
//Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.READ_PHONE_STATE,
};
public final String[] LOCATION_PERMISSIONS_2 = {
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG,"onCreate()");
Log.i(TAG, "onCreate()");
setContentView(R.layout.startup_activity);
// Set our custom uncaught exception handler to report issues.
//Thread.setDefaultUncaughtExceptionHandler(new OsdUncaughtExceptionHandler(StartupActivity.this));
@@ -86,20 +125,6 @@ public class StartupActivity extends Activity {
.addCommaSeparatedEmailAddresses("crashreports@openseizuredetector.org.uk,")
.build();
mHandler = new Handler();
mUtil = new OsdUtil(this, mHandler);
mUtil.writeToSysLogFile("");
mUtil.writeToSysLogFile("*******************************");
mUtil.writeToSysLogFile("* StartUpActivity Started *");
mUtil.writeToSysLogFile("*******************************");
// Force the screen to stay on when the app is running
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.startup_activity);
// Read the default settings from the xml preferences files, so we do
// not have to use the hard coded ones in the java files.
PreferenceManager.setDefaultValues(this, R.xml.alarm_prefs, true);
@@ -108,6 +133,17 @@ public class StartupActivity extends Activity {
PreferenceManager.setDefaultValues(this, R.xml.pebble_datasource_prefs, true);
PreferenceManager.setDefaultValues(this, R.xml.seizure_detector_prefs, true);
PreferenceManager.setDefaultValues(this, R.xml.network_passive_datasource_prefs, true);
PreferenceManager.setDefaultValues(this, R.xml.logging_prefs, true);
mHandler = new Handler();
mUtil = new OsdUtil(getApplicationContext(), mHandler);
mUtil.writeToSysLogFile("");
mUtil.writeToSysLogFile("*******************************");
mUtil.writeToSysLogFile("* StartUpActivity Started *");
mUtil.writeToSysLogFile("*******************************");
// Force the screen to stay on when the app is running
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Button b;
@@ -142,24 +178,16 @@ public class StartupActivity extends Activity {
}
});
mConnection = new SdServiceConnection(this);
mConnection = new SdServiceConnection(getApplicationContext());
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG,"onStart()");
Log.i(TAG, "onStart()");
mUtil.writeToSysLogFile("StartupActivity.onStart()");
TextView tv;
if (mUtil.arePermissionsOK()) {
Log.i(TAG,"onStart() - Permissions OK");
} else {
Log.i(TAG,"onStart() - Permissions Not OK - requesting them");
mUtil.requestPermissions(this);
}
String versionName = mUtil.getAppVersionName();
tv = (TextView) findViewById(R.id.appNameTv);
tv.setText("OpenSeizureDetector V" + versionName);
@@ -174,18 +202,36 @@ public class StartupActivity extends Activity {
if (mUtil.isServerRunning()) {
Log.i(TAG, "onStart() - server running - stopping it");
Log.i(TAG, "onStart() - server running - stopping it - isServerRunning="+mUtil.isServerRunning());
mUtil.writeToSysLogFile("StartupActivity.onStart() - server already running - stopping it.");
mUtil.stopServer();
} else {
Log.i(TAG, "onStart() - server not running - isServerRunning="+mUtil.isServerRunning());
}
// Wait 0.1 second to give the server chance to shutdown in case we have just shut it down below, then start it
mHandler.postDelayed(new Runnable() {
public void run() {
mUtil.writeToSysLogFile("StartupActivity.onStart() - starting server after delay - isServerRunning="+mUtil.isServerRunning());
Log.i(TAG, "onStart() - starting server after delay -isServerRunning="+mUtil.isServerRunning());
mUtil.startServer();
// Bind to the service.
Log.i(TAG, "onStart() - binding to server");
mUtil.writeToSysLogFile("StartupActivity.onStart() - binding to server");
mUtil.bindToServer(getApplicationContext(), mConnection);
}
}, 100);
// Check power management settings
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (powerManager.isIgnoringBatteryOptimizations(getPackageName())) {
Log.i(TAG, "Power Management OK - we are ignoring Battery Optimizations");
mBatteryOptDialogDisplayed = false;
} else {
Log.e(TAG, "Power Management Problem - not ignoring Battery Optimisations");
//mUtil.showToast("WARNING - Phone is Optimising OpenSeizureDetector Battery Usage - this is likely to prevent it working correctly when running on battery!");
if (!mBatteryOptDialogDisplayed) showBatteryOptimisationWarningDialog();
}
mUtil.writeToSysLogFile("StartupActivity.onStart() - starting server");
Log.i(TAG,"onStart() - starting server");
mUtil.startServer();
// Bind to the service.
Log.i(TAG,"onStart() - binding to server");
mUtil.writeToSysLogFile("StartupActivity.onStart() - binding to server");
mUtil.bindToServer(this, mConnection);
// Check to see if this is the first time the app has been run, and display welcome dialog if it is.
checkFirstRun();
@@ -198,7 +244,7 @@ public class StartupActivity extends Activity {
mHandler.post(serverStatusRunnable);
//updateServerStatus();
}
}, 0, 1000);
}, 0, 2000);
}
@@ -208,17 +254,17 @@ public class StartupActivity extends Activity {
super.onStop();
Log.i(TAG, "onStop() - unbinding from server");
mUtil.writeToSysLogFile("StartupActivity.onStop() - unbinding from server");
mUtil.unbindFromServer(this, mConnection);
mUtil.unbindFromServer(getApplicationContext(), mConnection);
mUiTimer.cancel();
}
/*
* serverStatusRunnable - called by updateServerStatus - updates the
* user interface to reflect the current status received from the server.
* If everything is ok, we close this activity and open the main user interface
* activity.
*/
* serverStatusRunnable - called by updateServerStatus - updates the
* user interface to reflect the current status received from the server.
* If everything is ok, we close this activity and open the main user interface
* activity.
*/
final Runnable serverStatusRunnable = new Runnable() {
public void run() {
Boolean allOk = true;
@@ -227,23 +273,49 @@ public class StartupActivity extends Activity {
boolean smsAlarmsActive = true;
boolean phoneAlarmsActive = true;
Log.v(TAG,"serverStatusRunnable()");
Log.v(TAG, "serverStatusRunnable()");
SharedPreferences SP = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
smsAlarmsActive = SP.getBoolean("SMSAlarm", false);
phoneAlarmsActive = SP.getBoolean("PhoneCallAlarm", false);
// Check power management settings
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (powerManager.isIgnoringBatteryOptimizations(getPackageName())) {
Log.i(TAG, "Power Management OK - we are ignoring Battery Optimizations");
if (mBatteryOptDialogDisplayed) {
mBatteryOptDialog.cancel();
mBatteryOptDialogDisplayed = false;
}
}
// Settings ok
tv = (TextView) findViewById(R.id.textItem1);
pb = (ProgressBar) findViewById(R.id.progressBar1);
if (mUtil.arePermissionsOK()) {
if (smsAlarmsActive && !mUtil.areSMSPermissionsOK()) {
if (arePermissionsOK()) {
if (smsAlarmsActive && !areSMSPermissions1OK()) {
Log.i(TAG,"SMS permissions NOT OK");
tv.setText(getString(R.string.SmsPermissionWarning));
tv.setBackgroundColor(okColour);
tv.setTextColor(okTextColour);
pb.setIndeterminateDrawable(getResources().getDrawable(R.drawable.start_server));
pb.setProgressDrawable(getResources().getDrawable(R.drawable.start_server));
mUtil.requestSMSPermissions(StartupActivity.this);
tv.setBackgroundColor(alarmColour);
tv.setTextColor(alarmTextColour);
//pb.setIndeterminateDrawable(getResources().getDrawable(R.drawable.start_server));
//pb.setProgressDrawable(getResources().getDrawable(R.drawable.start_server));
requestSMSPermissions();
allOk = false;
} else if (smsAlarmsActive && !areLocationPermissions1OK()) {
Log.i(TAG,"Location permissions NOT OK");
tv.setText(getString(R.string.SmsPermissionWarning));
tv.setBackgroundColor(alarmColour);
tv.setTextColor(alarmTextColour);
requestLocationPermissions1();
allOk = false;
} else if (smsAlarmsActive && !areLocationPermissions2OK()) {
Log.i(TAG,"SMS permissions2 NOT OK");
tv.setText(getString(R.string.SmsPermissionWarning));
tv.setBackgroundColor(alarmColour);
tv.setTextColor(alarmTextColour);
requestLocationPermissions2();
allOk = false;
} else {
tv.setText(getString(R.string.AppPermissionsOk));
tv.setBackgroundColor(okColour);
@@ -257,7 +329,7 @@ public class StartupActivity extends Activity {
tv.setTextColor(alarmTextColour);
pb.setIndeterminate(true);
allOk = false;
mUtil.requestPermissions(StartupActivity.this);
requestPermissions(StartupActivity.this);
}
// If phone alarms are selected, we need to have the uk.org.openseizuredetector.dialler package installed to do the actual dialling.
@@ -305,7 +377,6 @@ public class StartupActivity extends Activity {
}
// Do we have seizure detector data?
tv = (TextView) findViewById(R.id.textItem5);
pb = (ProgressBar) findViewById(R.id.progressBar5);
@@ -342,11 +413,10 @@ public class StartupActivity extends Activity {
}
// If all the parameters are ok, close this activity and open the main
// user interface activity instead.
if (allOk) {
if (!mDialogDisplayed) {
if (!mDialogDisplayed && !mBatteryOptDialogDisplayed) {
if (!mStartedMainActivity) {
Log.i(TAG, "serverStatusRunnable() - starting main activity...");
mUtil.writeToSysLogFile("StartupActivity.serverStatusRunnable - all checks ok - starting main activity.");
@@ -393,42 +463,65 @@ public class StartupActivity extends Activity {
}
}
/**
* checkFirstRun - checks to see if this is the first run of the app after installation or upgrade.
* if it is, the relevant dialog message is displayed. If not, the routine just exists so start-up can continue.
*/
/**
* checkFirstRun - checks to see if this is the first run of the app after installation or upgrade.
* if it is, the relevant dialog message is displayed. If not, the routine just exists so start-up can continue.
*/
public void checkFirstRun() {
String storedVersionName = "";
String versionName;
AlertDialog UpdateDialog;
AlertDialog FirstRunDialog;
SharedPreferences prefs;
Log.i(TAG,"checkFirstRun()");
Log.i(TAG, "checkFirstRun()");
versionName = this.getVersionName(this, StartupActivity.class);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
storedVersionName = (prefs.getString("AppVersionName", null));
Log.v(TAG,"storedVersionName="+storedVersionName+", versionName="+versionName);
Log.v(TAG, "storedVersionName=" + storedVersionName + ", versionName=" + versionName);
// CHeck for new installation
//storedVersionName = null; // FIXME Force first run dialog for easier testing ****************************
if (storedVersionName == null || storedVersionName.length() == 0) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
this);
final SpannableString s = new SpannableString(
getString(R.string.FirstRunDlgMsg)+getString(R.string.changelog)
);
// This makes the links display as links, but they do not respond to clicks for some reason...
Linkify.addLinks(s, Linkify.ALL);
final String s = new String(
getString(R.string.FirstRunDlgMsg));
alertDialogBuilder
.setTitle(getString(R.string.FirstRunDlgTitle))
.setMessage(s)
.setMessage(Html.fromHtml(s))
.setCancelable(false)
.setPositiveButton(getString(R.string.okBtnTxt), new DialogInterface.OnClickListener() {
.setNeutralButton(getString(R.string.closeBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
mDialogDisplayed = false;
//MainActivity.this.finish();
}
});
})
.setPositiveButton("Privacy Policy", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
mDialogDisplayed = false;
String url = OsdUtil.PRIVACY_POLICY_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
dialog.cancel();
mDialogDisplayed = false;
}
})
.setNegativeButton("Data Sharing", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
mDialogDisplayed = false;
String url = OsdUtil.DATA_SHARING_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
dialog.cancel();
mDialogDisplayed = false;
}
})
;
FirstRunDialog = alertDialogBuilder.create();
Log.i(TAG, "Displaying First Run Dialog");
FirstRunDialog.show();
@@ -437,19 +530,43 @@ public class StartupActivity extends Activity {
// Check for update of installed application
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
this);
final SpannableString s = new SpannableString(
getString(R.string.UpgradeMsg)+getString(R.string.changelog)
final String s = new String(
getString(R.string.UpgradeMsg) + getString(R.string.changelog)
);
// This makes the links display as links, but they do not respond to clicks for some reason...
Linkify.addLinks(s, Linkify.ALL);
alertDialogBuilder
.setTitle(getString(R.string.UpdateDialogTitleTxt))
.setMessage(s)
.setMessage(Html.fromHtml(s))
.setCancelable(false)
.setPositiveButton(getString(R.string.okBtnTxt), new DialogInterface.OnClickListener() {
.setNeutralButton(getString(R.string.closeBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
mDialogDisplayed = false;
//MainActivity.this.finish();
}
})
.setPositiveButton("Privacy Policy", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
mDialogDisplayed = false;
String url = OsdUtil.PRIVACY_POLICY_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
dialog.cancel();
mDialogDisplayed = false;
}
})
.setNegativeButton("Data Sharing", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
mDialogDisplayed = false;
String url = OsdUtil.DATA_SHARING_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
dialog.cancel();
mDialogDisplayed = false;
}
});
UpdateDialog = alertDialogBuilder.create();
@@ -463,4 +580,202 @@ public class StartupActivity extends Activity {
prefs.edit().putString("AppVersionName", versionName).commit();
}
private void showBatteryOptimisationWarningDialog() {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
this);
final SpannableString s = new SpannableString(
getString(R.string.battery_usage_optimisation_dialog_text)
);
// This makes the links display as links, but they do not respond to clicks for some reason...
Linkify.addLinks(s, Linkify.ALL);
alertDialogBuilder
.setTitle(R.string.battery_usage_optimisation_dialog_title)
.setMessage(s)
.setCancelable(false)
.setPositiveButton(getString(R.string.okBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
mBatteryOptDialogDisplayed = false;
}
});
mBatteryOptDialog = alertDialogBuilder.create();
Log.i(TAG, "Displaying Update Dialog");
mBatteryOptDialog.show();
mBatteryOptDialogDisplayed = true;
}
/*****************************************************************************/
public boolean arePermissionsOK() {
boolean allOk = true;
Log.v(TAG, "arePermissionsOK");
for (int i = 0; i < REQUIRED_PERMISSIONS.length; i++) {
if (ContextCompat.checkSelfPermission(this, REQUIRED_PERMISSIONS[i])
!= PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, REQUIRED_PERMISSIONS[i] + " Permission Not Granted");
allOk = false;
}
}
return allOk;
}
public boolean areSMSPermissions1OK() {
boolean allOk = true;
Log.v(TAG, "areSMSPermissions1 OK()");
for (int i = 0; i < SMS_PERMISSIONS_1.length; i++) {
if (ContextCompat.checkSelfPermission(this, SMS_PERMISSIONS_1[i])
!= PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "areSMSPermissions1OK: "+SMS_PERMISSIONS_1[i] + " Permission Not Granted");
allOk = false;
}
}
return allOk;
}
public boolean areLocationPermissions1OK() {
boolean allOk = true;
Log.v(TAG, "areLocationPermissions1 OK()");
for (int i = 0; i < LOCATION_PERMISSIONS_1.length; i++) {
if (ContextCompat.checkSelfPermission(this, LOCATION_PERMISSIONS_1[i])
!= PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, LOCATION_PERMISSIONS_1[i] + " Permission Not Granted");
allOk = false;
}
}
return allOk;
}
public boolean areLocationPermissions2OK() {
boolean allOk = true;
Log.v(TAG, "areSMSPermissions2OK()");
for (int i = 0; i < LOCATION_PERMISSIONS_2.length; i++) {
if (ContextCompat.checkSelfPermission(this, LOCATION_PERMISSIONS_2[i])
!= PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, LOCATION_PERMISSIONS_2[i] + " Permission Not Granted");
allOk = false;
}
}
return allOk;
}
public void requestPermissions(AppCompatActivity activity) {
if (mPermissionsRequested) {
Log.i(TAG, "requestPermissions() - request already sent - not doing anything");
} else {
Log.i(TAG, "requestPermissions() - requesting permissions");
for (int i = 0; i < REQUIRED_PERMISSIONS.length; i++) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
REQUIRED_PERMISSIONS[i])) {
Log.i(TAG, "shouldShowRationale for permission" + REQUIRED_PERMISSIONS[i]);
}
}
ActivityCompat.requestPermissions(activity,
REQUIRED_PERMISSIONS,
42);
mPermissionsRequested = true;
}
}
public void requestSMSPermissions() {
if (mSmsPermissionsRequested) {
Log.i(TAG, "requestSMSPermissions() - request already sent - not doing anything");
} else {
Log.i(TAG, "requestSMSPermissions() - requesting permissions");
mSmsPermissionsRequested = true;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
this);
alertDialogBuilder
.setTitle(R.string.permissions_required)
.setMessage(R.string.sms_permissions_rationale_1)
.setCancelable(false)
.setPositiveButton(getString(R.string.okBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
Log.i(TAG,"requestSMSPermissions(): Launching ActivityCompat.requestPermissions()");
ActivityCompat.requestPermissions(StartupActivity.this,
SMS_PERMISSIONS_1,
45);
}
})
.setNegativeButton(getString(R.string.cancelBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}).create().show();
}
}
public void requestLocationPermissions1() {
if (mLocationPermissions1Requested) {
Log.i(TAG, "requestLocationPermissions1() - request already sent - not doing anything");
} else {
Log.i(TAG, "requestLocationPermissions1() - requesting permissions");
mLocationPermissions1Requested = true;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
this);
alertDialogBuilder
.setTitle(R.string.permissions_required)
.setMessage(R.string.location_permissions_rationale_1)
.setCancelable(false)
.setPositiveButton(getString(R.string.okBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
Log.i(TAG, "requestLocationPermissions1(): Launching ActivityCompat.requestPermissions()");
ActivityCompat.requestPermissions(StartupActivity.this,
LOCATION_PERMISSIONS_1,
43);
}
})
.setNegativeButton(getString(R.string.cancelBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
})
.create().show();
}
}
public void requestLocationPermissions2() {
if (mLocationPermissions2Requested) {
Log.i(TAG, "requestSMSPermissions2() - request already sent - not doing anything");
} else {
Log.i(TAG, "requestSMSPermissions2() - requesting permissions");
mLocationPermissions2Requested = true;
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
this);
alertDialogBuilder
.setTitle(R.string.permissions_required)
.setMessage(R.string.location_permissions_2_rationale)
.setCancelable(false)
.setPositiveButton(getString(R.string.okBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
Log.i(TAG,"requestSMSPermissions(): Launching ActivityCompat.requestPermissions()");
ActivityCompat.requestPermissions(StartupActivity.this,
LOCATION_PERMISSIONS_2,
44);
}
})
.setNegativeButton(getString(R.string.cancelBtnTxt), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}).create().show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
Log.i(TAG, "onRequestPermissionsResult - Permission" + permissions + " = " + grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
for (int i = 0; i < permissions.length; i++) {
Log.i(TAG, "Permission " + permissions[i] + " = " + grantResults[i]);
}
}
}

View File

@@ -0,0 +1,234 @@
package uk.org.openseizuredetector;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
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 com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.firestore.core.OrderBy;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
// This class is intended to handle all interactions with the OSD WebAPI
public abstract class WebApiConnection {
protected Context mContext;
protected OsdUtil mUtil;
private String TAG = "WebApiConnection";
private String mAuthToken;
public interface JSONObjectCallback {
public void accept(JSONObject retValObj);
}
public interface StringCallback {
public void accept(String retValStr);
}
public interface LongCallback {
public void accept(Long retVal);
}
public WebApiConnection(Context context) {
mContext = context;
mUtil = new OsdUtil(mContext, new Handler());
}
public void close() {
Log.i(TAG, "stop()");
}
public abstract boolean isLoggedIn();
// 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 abstract boolean createEvent(final int osdAlarmState, final Date eventDate, final String type, final String subType,
final String eventDesc, final String dataJSON, StringCallback callback);
// calls function callback with a JSONObject representation of the event with id 'eventId'
public abstract boolean getEvent(String eventId, JSONObjectCallback callback);
/**
* 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 abstract boolean getEvents(JSONObjectCallback callback);
public abstract boolean updateEvent(final JSONObject eventObj, JSONObjectCallback callback);
public abstract boolean createDatapoint(JSONObject dataObj, String eventId, StringCallback callback);
/**
* Retrieve the file containing the standard event types from the server.
* 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 abstract boolean getEventTypes(JSONObjectCallback callback);
/**
* 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 abstract boolean checkServerConnection();
public abstract boolean getUserProfile(JSONObjectCallback callback);
public boolean authenticate(final String uname, final String passwd, StringCallback callback) {
Log.e(TAG, "WebApiConnection.authenticate(username, password, callback) Not Implemented");
return false;
}
// 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);
}
/**
* Mark all of the events with IDs contained in eventList as unknown type.
* @param eventList list of String IDs of the events to mark as unknown.
* @return true if request sent successfully or false.
*/
private boolean markEventsAsUnknown(ArrayList<String>eventList) {
if (eventList.size()>0) {
Log.i(TAG,"markEventsAsUnknown - eventList.size()="+eventList.size());
Log.i(TAG,"markEventsAsUnknown - eventList(0) = "+eventList.get(0));
getEvent(eventList.get(0), new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventObj) {
Log.v(TAG, "markEventsAsUnknown.getEvent.callback: "+eventObj);
if (eventObj != null) {
Log.v(TAG, "markEventsAsUnknown.getEvent.callback: eventObj=" + eventObj.toString());
try {
eventObj.put("type", "Unknown");
String notesStr = eventObj.getString("desc");
if (notesStr == null) notesStr = new String("");
notesStr = notesStr + " Set to Unknown automatically by OSD Android App";
eventObj.put("desc", notesStr);
updateEvent(eventObj,new WebApiConnection.JSONObjectCallback() {
@Override
public void accept(JSONObject eventObj) {
if (eventObj != null) {
Log.i(TAG, "markEventsAsUnknown.updateEvent.callback" + eventObj.toString());
// Remove the first item from the list,then call this whole procedure again to modify the next one on the list.
eventList.remove(0);
markEventsAsUnknown(eventList);
} else {
Log.e(TAG, "markEventsAsUnknown.updateEvent.callback - eventObj is null");
mUtil.showToast("markEventsAsUnknown.updateEvent.callback - eventObj is null");
}
}
});
} catch (JSONException e) {
Log.e(TAG,"markEventsAsUnknown.getEvent.callback: Error editing eventObj");
mUtil.showToast("markEventsAsUnknown.getEvent.callback: Error editing eventObj");
}
} else {
mUtil.showToast("Failed to Retrieve Event from Remote Database");
return;
}
}
});
} else {
Log.i(TAG,"markEventsAsUnknown(): No more events to Modify");
mUtil.showToast("No more unvalidated events to modify.");
}
return(true);
}
/**
* Mark all unverified events in the remote database as unknown
*
* @return true if request is successful or false.
*/
public boolean markUnverifiedEventsAsUnknown() {
if (getEvents((JSONObject remoteEventsObj) -> {
Log.v(TAG, "markUnverifiedEventsAsUnknown.getEvents.Callback()");
Boolean haveUnvalidatedEvent = false;
if (remoteEventsObj == null) {
Log.e(TAG, "markUnverifiedEventsAsUnknown.getEvents.Callback: Error Retrieving events");
} else {
try {
JSONArray eventsArray = remoteEventsObj.getJSONArray("events");
ArrayList<String> unvalidatedEventsList = new ArrayList<String>();
for (int i = eventsArray.length() - 1; i >= 0; i--) {
JSONObject eventObj = eventsArray.getJSONObject(i);
String typeStr = eventObj.getString("type");
if (typeStr.equals("null") || typeStr.equals("")) {
haveUnvalidatedEvent = true;
unvalidatedEventsList.add(eventObj.getString("id"));
}
}
Log.v(TAG, "markUnverifiedEventsAsUnknown.getEvents.onFinish.callback - haveUnvalidatedEvent = " +
haveUnvalidatedEvent);
markEventsAsUnknown(unvalidatedEventsList);
} catch (JSONException e) {
Log.e(TAG, "markUnverifiedEventsAsUnknown.getEvents.onFinish(): Error Parsing remoteEventsObj: " + e.getMessage());
//mUtil.showToast("Error Parsing remoteEventsObj - this should not happen!!!");
}
}
})) {
Log.v(TAG, "markUnverifiedEventsAsUnknown.getEvents - requested events");
} else {
Log.v(TAG, "markUnverifiedEventsAsUnknown.getEvents - Not Logged In");
}
return (true);
}
}

Some files were not shown because too many files have changed in this diff Show More