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

@@ -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);
}
}

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -2,11 +2,11 @@
<!-- Copyright (C) 2009 The Android Open Source Project
http://www.apache.org/licenses/LICENSE-2.0
-->
<ScrollView>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -21,15 +21,5 @@
android:text="@string/about_text"
/>
<TextView
android:id="@+id/about_credits"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="20dip"
android:textSize="16sp"
android:text="@string/credits_text"
android:autoLink="web"
/>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,214 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:contentDescription="@string/app_name"
android:scaleType="center"
app:srcCompat="@drawable/star_of_life_24x24" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_to_osdapi"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
<LinearLayout
android:id="@+id/login_ui"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/login_osdapi_ui"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginTop="16dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="username" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginTop="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="16dp"
android:fontFamily="sans-serif"
android:hint="password"
android:inputType="textPassword" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/cancelBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/back" />
<Button
android:id="@+id/loginBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/authenticate" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/RegisterBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/register" />
<Button
android:id="@+id/ResetPasswordBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/reset_password" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/logout_ui"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/logged_in_as_user_id" />
<TextView
android:id="@+id/userIdTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="userId" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text=" (" />
<TextView
android:id="@+id/usernameTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="username" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text=")" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/logoutCancelBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/back" />
<Button
android:id="@+id/logoutBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/logout" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="?android:attr/listDivider" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/aboutDataSharingBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/about_data_sharing" />
<Button
android:id="@+id/privacyPolicyBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/privacy_policy" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/cancelBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
android:id="@+id/loginBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/save" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="EventId: " />
<TextView
android:id="@+id/eventIdTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="[id]" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Event Date: "
android:textSize="20sp" />
<TextView
android:id="@+id/eventDateTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="..."
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Alarm State: "
android:textSize="20sp" />
<TextView
android:id="@+id/eventAlarmStateTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="..."
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/event_type"
android:textSize="20sp" />
<RadioGroup
android:id="@+id/eventTypeRg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></RadioGroup>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/event_sub_type"
android:textSize="20sp" />
<RadioGroup
android:id="@+id/eventSubTypeRg"
android:layout_width="match_parent"
android:layout_height="wrap_content"></RadioGroup>
</LinearLayout>
<EditText
android:id="@+id/eventNotsTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginTop="16dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="notes about event" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="uk.org.openseizuredetector.LogManager">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/local_database"
android:textAppearance="?android:attr/textAppearanceLarge" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/num_local_events" />
<TextView
android:id="@+id/num_local_events_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="000" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/num_local_datapoints" />
<TextView
android:id="@+id/num_local_datapoints_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="000" />
</LinearLayout>
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/shared_data_rb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:onClick="onRadioButtonClicked"
android:text="@string/shared_data" />
<RadioButton
android:id="@+id/local_data_rb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:onClick="onRadioButtonClicked"
android:text="@string/local_data" />
<RadioButton
android:id="@+id/syslog_rb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:onClick="onRadioButtonClicked"
android:text="@string/system_logs" />
</RadioGroup>
<LinearLayout
android:visibility="visible"
android:id="@+id/shared_data_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/remote_database"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/check_seizures_message"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/authStatusTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_authenticated" />
<Button
android:id="@+id/auth_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/authenticate" />
<Button
android:id="@+id/refresh_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/refreshBtn" />
</LinearLayout>
<ListView
android:id="@+id/remoteEventsLv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
<LinearLayout
android:visibility="gone"
android:id="@+id/local_data_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/EventsInLocalDb"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="@+id/eventLogListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
<LinearLayout
android:visibility="gone"
android:id="@+id/syslog_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/system_logs"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="@+id/sysLogListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
@@ -10,36 +11,34 @@
tools:context="uk.org.openseizuredetector.LogManager">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/remote_database"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/not_authenticated"
android:id="@+id/authStatusTv"
/>
<Button
android:id="@+id/authenticate_button"
android:id="@+id/auth_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/authenticate" />
</LinearLayout>
<!--
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/eventLogListView"
android:layout_marginTop="97dp" />
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:inputType="textPersonName"
android:text="Name" />
-->
<WebView
android:id="@+id/remote_db_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>

View File

@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="uk.org.openseizuredetector.LogManager">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/report_seizure"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/date" />
<TextView
android:id="@+id/date_day_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dd" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:text="/" />
<TextView
android:id="@+id/date_mon_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="mm" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:text="/" />
<TextView
android:id="@+id/date_year_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="yyyy" />
<Button
android:id="@+id/select_date_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/select_date" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/time" />
<TextView
android:id="@+id/time_hh_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hh" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:text=":" />
<TextView
android:id="@+id/time_mm_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="mm" />
<Button
android:id="@+id/select_time_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/select_time" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/event_type"
android:textSize="20sp" />
<RadioGroup
android:id="@+id/eventTypeRg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></RadioGroup>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/event_sub_type"
android:textSize="20sp" />
<RadioGroup
android:id="@+id/eventSubTypeRg"
android:layout_width="match_parent"
android:layout_height="wrap_content"></RadioGroup>
</LinearLayout>
<EditText
android:id="@+id/eventNotesTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginTop="16dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="notes about event" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/loginBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/okBtnTxt" />
<Button
android:id="@+id/cancelBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancelBtnTxt" />
</LinearLayout>
<TextView
android:id="@+id/msg_tv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="msg" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/ble_scan_status_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16pt"
android:text="ble_scan_status_tv" />
<Button
android:id="@+id/startScanButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onScanButtonClick"
android:text="Start Scan"/>
</LinearLayout>
<TextView
android:id="@+id/ble_present_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ble_present_tv" />
<TextView
android:id="@+id/ble_adapter_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ble_adapter_tv" />
<TextView
android:id="@+id/current_ble_device_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="current_ble_device_tv" />
<TextView
android:id="@+id/ble_perm1_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ble_perm1_tv" />
<TextView
android:id="@+id/ble_perm2_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ble_perm2_tv" />
<TextView
android:id="@+id/ble_perm3_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ble_perm3_tv" />
<TextView
android:id="@+id/ble_perm4_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ble_perm4_tv" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#00FF00"
android:drawSelectorOnTop="false" />
<TextView
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0000"
android:text="No data" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
http://www.apache.org/licenses/LICENSE-2.0
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dip">
<!--
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:autoLink="web"
android:text="@string/datasharing_about_title"
/>
-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="20dip"
android:textSize="16sp"
android:text="@string/datasharing_about_text"
android:autoLink="web"
/>
</LinearLayout>
</ScrollView>

View File

@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:src="@drawable/star_of_life_24x24"
android:layout_width="match_parent"
android:layout_height="64dp"
android:scaleType="center"
android:background="#FFFFBB33"
android:contentDescription="@string/app_name" />
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="username" />
<EditText
android:id="@+id/password"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="16dp"
android:fontFamily="sans-serif"
android:hint="password"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/cancelBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
android:id="@+id/OKBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/authenticate" />
</LinearLayout>
</LinearLayout>

View File

@@ -3,7 +3,7 @@
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
@@ -11,24 +11,32 @@
android:layout_height="wrap_content"
android:text="date"
android:id="@+id/event_date" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" : "/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alarm"
android:id="@+id/event_alarmState"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="note"
android:id="@+id/event_note"
/>
</LinearLayout>
android:text=" : "/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="uploaded"
android:id="@+id/event_uploaded"
/>
</LinearLayout>
<!-- <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dataJSON"
android:id="@+id/event_dataJSON" />
-->
</LinearLayout>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/event_date_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="date"
android:textStyle="bold" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/event_type_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="---"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" : " />
<TextView
android:id="@+id/event_subtype_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="---" />
</LinearLayout>
<TextView
android:id="@+id/event_alarmState_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alarm" />
<TextView
android:id="@+id/event_notes_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="notes"
android:textStyle="italic" />
<TextView
android:id="@+id/event_id_remote_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
android:text="eventId" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -20,16 +20,16 @@
android:id="@+id/serverStatusTv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="0.5"
android:gravity="center"
android:text="textView1" />
<TextView
android:id="@+id/serverIpTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="0.5"
android:gravity="center"
android:text="textView2" />
</LinearLayout>
@@ -86,6 +86,11 @@
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/remoteDbTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="----" />
<TextView
android:id="@+id/powerTv"
@@ -116,19 +121,30 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:baselineAligned="false">
<Button
android:id="@+id/acceptAlarmButton"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/AcceptAlarmBtnTxt" />
<Button
android:id="@+id/cancelAudibleButton"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/CancelAudibleButtonTxt" />
<Button
android:id="@+id/manualAlarmButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="#ff0000"
android:text="@string/ManualAlarmBtnTxt" />
</LinearLayout>
<com.github.mikephil.charting.charts.BarChart

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -15,7 +16,7 @@
android:layout_height="wrap_content"
android:id="@+id/imageView"
android:layout_gravity="center_horizontal"
android:src="@drawable/star_of_life_48x48" />
app:srcCompat="@drawable/star_of_life_48x48" />
<TextView
android:layout_width="wrap_content"

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/syslog_entry_date_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="date" />
<TextView
android:id="@+id/syslog_level_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="---" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="date"
android:id="@+id/syslog_entry_text_tv" />
</LinearLayout>

View File

@@ -0,0 +1,31 @@
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<group android:id="@+id/grp1">
<item
android:id="@+id/pruneDatabaseMenuItem"
app:showAsAction="never|withText"
android:title="@string/prune_database" />
<item
android:id="@+id/action_mark_unknown"
app:showAsAction="never|withText"
android:title="@string/mark_unverified_events_as_unknown" />
</group>
<group android:id="@+id/grp3">
<item
android:id="@+id/action_report_seizure"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
app:showAsAction="never|withText"
android:title="@string/report_seizure" />
<item
android:id="@+id/action_authenticate_api"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
app:showAsAction="never|withText"
android:title="@string/data_sharing_log_in" />
</group>
</menu>

View File

@@ -1,92 +1,104 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_accept_alarm"
android:showAsAction="never|withText"
android:title="@string/accept_alarm" />
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<group android:id="@+id/grp1">
<item
android:id="@+id/action_accept_alarm"
app:showAsAction="never|withText"
android:title="@string/accept_alarm" />
</group>
<group android:id="@+id/grp2">
<item
android:id="@+id/action_start_stop"
android:icon="@drawable/stop_server"
app:showAsAction="never|withText"
android:title="@string/start_stop_server" />
</group>
<group android:id="@+id/grp3">
<item
android:id="@+id/action_report_seizure"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
app:showAsAction="never|withText"
android:title="@string/report_seizure" />
<item
android:id="@+id/action_logmanager"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
app:showAsAction="never|withText"
android:title="@string/data_log_manager" />
<item
android:id="@+id/action_authenticate_api"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
app:showAsAction="never|withText"
android:title="@string/data_sharing_log_in" />
<item
android:id="@+id/action_about_datasharing"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
app:showAsAction="never|withText"
android:title="About Data Sharing..." />
</group>
<group android:id="@+id/grp4">
<item
android:id="@+id/action_install_watch_app"
app:showAsAction="never|withText"
android:title="@string/install_watch_app" />
<item
android:id="@+id/action_test_alarm_beep"
android:icon="@drawable/stop_server"
app:showAsAction="never|withText"
android:title="@string/test_alarm_beep" />
<item
android:id="@+id/action_test_warning_beep"
android:icon="@drawable/stop_server"
app:showAsAction="never|withText"
android:title="@string/test_warning_beep" />
<item
android:id="@+id/action_test_sms_alarm"
android:icon="@drawable/stop_server"
app:showAsAction="never|withText"
android:title="@string/test_sms_alarm_notification" />
</group>
<group android:id="@+id/grp5">
<!--<item
android:id="@+id/action_logs"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
android:showAsAction="never|withText"
android:title="@string/view_log_entries" /> -->
<!--
<item
android:enabled="false"
android:id="@+id/action_export"
android:icon="@drawable/ic_action_settings"
android:showAsAction="never|withText"
android:title="@string/export_data"
/>
-->
<item
android:id="@+id/action_settings"
android:icon="@drawable/ic_action_settings"
app:showAsAction="never|withText"
android:title="@string/settings" />
</group>
<group android:id="@+id/grp6">
<item
android:id="@+id/action_about"
app:showAsAction="never|withText"
android:title="@string/about" />
</group>
<item
android:id="@+id/action_start_stop"
android:icon="@drawable/stop_server"
android:showAsAction="never|withText"
android:title="@string/start_stop_server" />
<!-- fault beep test does not work because of fault timer so don't show menu option
<item
android:id="@+id/action_test_fault_beep"
android:icon="@drawable/stop_server"
android:showAsAction="never|withText"
android:title="Test Fault Beep" />
-->
<!-- <item
android:id="@+id/action_launch_pebble_app"
android:showAsAction="never|withText"
android:title="Launch Pebble App" />
-->
<item
android:id="@+id/action_install_watch_app"
android:showAsAction="never|withText"
android:title="@string/install_watch_app" />
<item
android:id="@+id/action_test_alarm_beep"
android:icon="@drawable/stop_server"
android:showAsAction="never|withText"
android:title="@string/test_alarm_beep" />
<item
android:id="@+id/action_test_warning_beep"
android:icon="@drawable/stop_server"
android:showAsAction="never|withText"
android:title="@string/test_warning_beep" />
<item
android:id="@+id/action_test_sms_alarm"
android:icon="@drawable/stop_server"
android:showAsAction="never|withText"
android:title="@string/test_sms_alarm_notification" />
<item
android:enabled="false"
android:id="@+id/action_test_phone_alarm"
android:icon="@drawable/stop_server"
android:showAsAction="never|withText"
android:title="@string/test_phone_alarm_notification" />
<item
android:enabled="false"
android:id="@+id/action_logmanager"
android:icon="@drawable/ic_action_settings"
android:showAsAction="never|withText"
android:title="@string/data_log_manager"
/>
<item
android:id="@+id/action_logs"
android:icon="@drawable/ic_action_settings"
android:showAsAction="never|withText"
android:title="@string/view_log_entries"
android:enabled="true"
/>
<item
android:enabled="false"
android:id="@+id/action_export"
android:icon="@drawable/ic_action_settings"
android:showAsAction="never|withText"
android:title="@string/export_data"
/>
<item
android:id="@+id/action_settings"
android:icon="@drawable/ic_action_settings"
android:showAsAction="never|withText"
android:title="@string/settings"
/>
<item
android:id="@+id/action_about"
android:showAsAction="never|withText"
android:title="@string/about" />
</menu>
</menu>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -105,7 +105,6 @@
<string name="sms_location_alarm_active">SMS Location Alarm Active</string>
<string name="sms_location_alarm_disabled">SMS Location Alarm Disabled</string>
<string name="cancel_audible_not_sending_sms">Cancel Audible Active - not sending SMS</string>
<string name="sms_alarm_disabled">SMS Alarms Disabled - not doing anything!</string>
<string name="no_wifi_connection">Network State Changed - no Wifi Connection</string>
<string name="no_active_network">Network State Changed - No Active Network</string>
<string name="problem_parsing_preferences">Problem Parsing Preferences - Something won\'t work - Please go back to Settings and correct it!</string>

View File

@@ -120,7 +120,6 @@
<string name="sms_location_alarm_active">Alarma de Localización de SMS Activa</string>
<string name="sms_location_alarm_disabled">Alarma de Localización de SMS Inactiva</string>
<string name="cancel_audible_not_sending_sms">Cancele Alarma Audible - no enviando SMS</string>
<string name="sms_alarm_disabled">Alarmas de SMS Incativas - No está haciendo nada ! </string>
<string name="no_wifi_connection">Cambio en el Estado de Red - Sin Conexión de Wifi</string>
<string name="no_active_network">Cambio en el Estado de Red - Sin Red Activa</string>
<string name="problem_parsing_preferences">Problema al Interpretar las Preferencias - Algo no está Trabajando - Por favor regrese a Ajustes y corríjalo !</string>

View File

@@ -113,7 +113,7 @@
<string name="BasicPrefSummary">Ustawienia podstawowe</string>
<string name="accept_alarm">Zaakceptuj alarm</string>
<string name="start_stop_server">Włącz/wyłącz serwer</string>
<string name="install_watch_app">Zainstaluj apkę na zegarek</string>
<string name="install_watch_app">Pobierz apkę na zegarek</string>
<string name="test_alarm_beep">Test alarmu dźwiękowego</string>
<string name="test_warning_beep">Test dźwięku ostrzeżeń</string>
<string name="test_sms_alarm_notification">Wyślij próbny alarm SMS</string>
@@ -128,7 +128,6 @@
<string name="sms_location_alarm_active">SMS będzie zawierać lokalizację</string>
<string name="sms_location_alarm_disabled">SMS nie będzie zawierać lokalizacji</string>
<string name="cancel_audible_not_sending_sms">Wyłączono alarmy dźwiękowe - nie wysyłam SMS</string>
<string name="sms_alarm_disabled">Wyłączono alarmy SMS - nic nie zrobię!</string>
<string name="no_wifi_connection">Stan sieci zmieniony - brak połączenia Wifi</string>
<string name="no_active_network">Stan sieci zmieniony - brak aktywnych sieci</string>
<string name="problem_parsing_preferences">Problem z ustawieniami - to nie zadziała - proszę wrócić do ustawień i poprawić to!</string>
@@ -276,7 +275,7 @@
<string name="AlarmFunctionalitySettingsTitle">Ustawienia alarmów</string>
<string name="AudibleAlarmSettingsTitle">Ustawienia alarmów dźwiękowych</string>
<string name="SMSAlarmSettingsTitle">Ustawienia alarmów SMS</string>
<string name="DefaultSMSMsgText">**SPRAWDŹ PODOPIECZNEGO**</string>
<string name="DefaultSMSMsgText">**ALARM! SPRAWDŹ PODOPIECZNEGO!**</string>
<string name="AlarmLoggingTitle">Dziennik alarmów</string>
<string name="WarnTimeSummary">Czas oczekiwania przed włączeniem ostrzeżenia (domyślnie = 5 sek)</string>
<string name="WarnTimeTitle">Opóźnienie ostrzeżenia (sek)</string>

View File

@@ -0,0 +1,351 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">OpenSeizureDetector</string>
<string name="changelog">
"V4.0 - Dodano logiranje podatkov na strežnik.
\nV3.7.0 - Dodana podpora za Garmin meritve sauturacije kisika v krvi.
\nV3.6.2 - Popravek težav s pravicami datotek logiranja na nekaterih napravah Android 10 in dodanih nekaj prevedljivih besedil vmesnika.
\nV3.6.1 - Morebiten popravek težave ugašanja sistema.
\nV3.6 - Dodan podatkovni vir senzorjev telefona za testiranje brez pametnih ur.
\nV3.5 - Dodana podpora za SMS Annunciator App
\nV3.4 - Dodana podpora za podatkovne vire BLE"</string>
<string name="UpgradeMsg">
OpenSeizureDetector ne zbira osebnih podatkov.
To pomeni, da ne morem kontaktirati uporabnikov, če naletim na težavo z aplikacijo,
ki bi se jo morali zavedati.\n
Prosim, naročite se na ažurne informacije na http://openseizuredetector.org.uk ali na https://www.facebook.com/openseizuredetector,
tako da vas lahko obvestim, če bo potrebno.\nHvala! Graham \ngraham@openseizuredetector.org.uk
\n\nSpremembe v tej verziji:
\n
</string>
<string name="FirstRunDlgMsg">
OpenSeizureDetector ne zbira osebnih podatkov.
To pomeni, da ne morem kontaktirati uporabnikov, če naletim na težavo z aplikacijo,
ki bi se jo morali zavedati.\n
Prosim, naročite se na ažurne informacije na http://openseizuredetector.org.uk ali na https://www.facebook.com/openseizuredetector,
tako da vas lahko obvestim, če bo potrebno.\nHvala! Graham \ngraham@openseizuredetector.org.uk
\n\nSpremembe v tej verziji:
</string>
<string name="ask_for_error_log">Pardon, OpenSeizureDetector se je zrušil. Prosim, pošljite ta log po elektronski pošti, da lahko ugotovimo, kako odpraviti težavo.\nHvala, Graham.</string>
<string name="email_welcome_note">Dragi OpenSeizureDetector,\n\nAplikacija se je ravnokar zrušila, prosim preverite priloženi log za podrobnosti.\n\n\n</string>
<string name="copyright_info">OpenSeizureDetector (Uporablja UCE Handler\nCopyright © 2018 Rohit Sahebrao Surwase.)</string>
<string name="okBtnTxt">OK</string>
<string name="UpdateDialogTitleTxt">Hvala za posodobitev OpenSeizureDetector</string>
<string name="FirstRunDlgTitle">Dobrodošli v OpenSeizureDetector</string>
<string name="SmsPermissionWarning">Problem z SMS dovoljenji</string>
<string name="AppPermissionsOk">App dovoljenja OK</string>
<string name="AppPermissionsWarning">Problem z App dovoljenji</string>
<string name="BoundToServiceOk">Povezava na storitev OK</string>
<string name="BindingToService">Povezovanje na storitev v ozadnju ...</string>
<string name="WatchConnectedOk">Ura povezana OK</string>
<string name="WatchNotConnected">Ura NI povezana</string>
<string name="SeizureDetectorDataReceived">Podatki detektorja napadov sprejeti OK</string>
<string name="WaitingForSeizureDetectorData">Čakanje podatkov detektorja napadov ...</string>
<string name="SeizureDetectorSettingsReceived">Nastavitve detektorja napadov sprejeti OK</string>
<string name="WaitingForSeizureDetectorSettings">Čakanje nastavitev detektorja napadov ...</string>
<string name="DataSource">Vir podatkov</string>
<string name="AppTitleText">OpenSeizureDetector Android App Verzija </string>
<string name="ServerRunningOK">Strežnik deluje OK\n</string>
<string name="AccessServerAt">Dostopanje strežnika na </string>
<string name="ServerStopped">Strežnik ustavljen</string>
<string name="Warning">OPOZORILO</string>
<string name="Mute">TIHO</string>
<string name="Alarm">**ALARM**</string>
<string name="Fall">**PADEC**</string>
<string name="HR_Equals">Srčni utrip = </string>
<string name="HRAlarmOff">Alarm Srčnega utripa IZKLJUČEN</string>
<string name="WatchAppOK">App ure OK</string>
<string name="WatchAppNotRunning">App ure NE dela</string>
<string name="WatchBatteryEquals">Baterija ure = </string>
<string name="PowerEquals">Moč = </string>
<string name="SpectrumRatioEquals">Razmerje spektra = </string>
<string name="Threshold">Prag</string>
<string name="NetFault">NAPAKA OMREŽJA</string>
<string name="WatchApp">App ure -----</string>
<string name="Dashes">------</string>
<string name="SMSWillBeSentIn">SMS bo poslan v </string>
<string name="Cancel">PREKLIC?</string>
<string name="AcceptAlarm">Sprejmi Alarm</string>
<string name="AudibleAlarmsCancelledFor">Utišano za </string>
<string name="PressToReEnable"> Obnovi</string>
<string name="CancelAudibleAlarms">Utišaj alarme</string>
<string name="AudibleAlarmsOff">Zvočni Alarmi IZKLJUČENI</string>
<string name="Fault">NAPAKA</string>
<string name="authenticate">Prijava</string>
<string name="not_authenticated">Neprijavljen</string>
<string name="cancel">Preklic</string>
<!-- Strings related to login -->
<string name="prompt_email">Email</string>
<string name="prompt_password">Geslo</string>
<string name="action_sign_in">Prijava ali registracija</string>
<string name="action_sign_in_short">Prijava</string>
<string name="welcome">"Dobrodošli!"</string>
<string name="invalid_username">Uporabniško ime ni veljavno</string>
<string name="invalid_password">Geslo mora biti >5 znakov</string>
<string name="login_failed">"Prijava neuspešna"</string>
<string name="AdvancedModeTitle">Napredni Način</string>
<string name="AdvancedModeSummary">Omogoči Napredni način (prikaže mnogo dodatnih nastavitev ...)</string>
<string name="AutoStartTitle">Samodejni Zagon</string>
<string name="AutoStartSummary">OpenSeizureDetector se samodejno zažene ob zagonu telefona</string>
<string name="DataSourceTitle">Izberi Vir Podatkov</string>
<string name="DataSourceSummary">Izberi, kaj naj se uporabi za vir podatkov detekcije napadov (ura Garmin, ura Pebble, ali omrežna povezava)</string>
<string name="LogRemoteTitle">Oddaljeno Logiranje Podatkov</string>
<string name="LogRemoteSummary">Logiranje podatkov v centralno bazo OpenSeizureDetector</string>
<string name="UseMobileTitle">Uporabi Mobilni Internet</string>
<string name="UseMobileSummary">Uporabi mobilni internet za logiranje podatkov</string>
<string name="SeizureDetectorSettingsTitle">Nastavitve Detektorja Napadov</string>
<string name="AlarmThreshTitle">Prag Alarma</string>
<string name="AlarmThreshSummary">Prag alarma (Privzeto = 100)</string>
<string name="AlarmRatioThreshTitle">Prag Razmerja Alarma</string>
<string name="AlarmRatioThreshSummary">Prag razmerja alarma (Privzeto = 50). Povečaj za zmanjšanje občutljivosti.</string>
<string name="AlarmFreqMaxTitle">Max Frekv. Alarma (Hz)</string>
<string name="AlarmFreqMaxSummary">Najvišja frekvenca ROI (Hz) (Privzeto = 8 Hz)</string>
<string name="AlarmFreqMinTitle">Min frekv. Alarma (Hz)</string>
<string name="AlarmFreqMinSummary">Najnižja frekvenca ROI (Hz) (Privzeto = 3 Hz)</string>
<string name="HRAlarmEnabledTitle">Alarm Srčnega Utripa Omogočen</string>
<string name="HRAlarmEnabledSummary" />
<string name="HRNullAlarmTitle">Obravnavaj stanje brez srčnega utripa kot alarm</string>
<string name="HRNullAlarmSummary" />
<string name="HRThreshMinTitle">Min prag Srčnega Utripa (utripov/min)</string>
<string name="HRThreshMinSummary" />
<string name="HRThreshMaxTitle">Max prag Srčnega Utripa (utripov/min)</string>
<string name="HRThreshMaxSummary" />
<string name="BasicPrefTitle">Osnovno</string>
<string name="BasicPrefSummary">Osnovne preference</string>
<string name="accept_alarm">Sprejmi Alarm</string>
<string name="start_stop_server">Start/Stop Strežnik</string>
<string name="install_watch_app">Namesti app ure</string>
<string name="test_alarm_beep">Testiraj pisk alarma</string>
<string name="test_warning_beep">Testiraj pisk opozorila</string>
<string name="test_sms_alarm_notification">Testiraj SMS opozorilo alarma</string>
<string name="data_log_manager">Upravljanje podatkov logiranja</string>
<string name="view_log_entries">Pregled podatkov logiranja</string>
<string name="export_data">Izvažanje podatkov</string>
<string name="settings">Nastavitve</string>
<string name="about">O appu ...</string>
<string name="about_text">OpenSeizureDetector je detektor epileptičnih (konvulzivnih) napadov in sistem alarmov. Uporablja pametno uro za detekcijo tresenja povezanega z napadom, nakar sproži glasovna in SMS opozorila za skrbnike. Sistem je brezplačen in odprtokoden - več na http://openseizuredetector.org.uk. Prosim sporočite težave na graham@openseizuredetector.org.uk\n ali prijavite težavo na github projektu odprte kode, repozitorij - https://github.com/OpenSeizureDetector</string> <!-- preveri "konvulzivnost" z Grahamom -->
<string name="credits_text">App glavne ure in Android App \n copyright Graham Jones, 2015.\n Uporabljene so sledeče knjižnice :\n - SYLT-FFT - https://github.com/stg/SYLT-FFT by D. Taylor.\n - NanoHTTPD - https://github.com/NanoHttpd/nanohttpd\n - jQuery - http://jquery.org\n - jBeep - http://www.ultraduz.com.br\n - Chartjs - http://www.chartjs.org\n - MPAndroidChart - https://github.com/PhilJay/MPAndroidChart\n - UCE-Handler - https://github.com/RohitSurwase/UCE-Handler\n \n Logotip temelji na Star of life2, avtorja Verdy P, \n Licensed under Public Domain via\n Wikimedia Commons (http://commons.wikimedia.org/wiki/File:Star_of_life2.svg#mediaviewer/File:Star_of_life2.svg).</string>
<string name="edit_settings">Urejanje nastavitev</string>
<string name="sms_location_alarm_active">SMS alarm lokacije Aktiven</string>
<string name="sms_location_alarm_disabled">SMS alarm lokacije Neaktiven</string>
<string name="cancel_audible_not_sending_sms">Cancel Audible Active - not sending SMS</string> <!-- HMMM, what does it mean?? -->
<!--<string name="sms_alarm_disabled">SMS alarmi Onemogočeni - ne javlja nič!</string>-->
<string name="no_wifi_connection">Spremenjeno stanje omrežja - ni Wifi povezave</string>
<string name="no_active_network">Spremenjeno stanje omrežja - Ni aktivne povezave</string>
<string name="problem_parsing_preferences">Težava pri razčlenjevanju nastavitev - Nekaj ne bo delalo - Prosim vrnite se v nastavitve in popravite!</string>
<string name="send_sms_last_location">Pošiljanje SMS - zadnja lokacija je </string>
<string name="failed_to_send_sms">NAPAKA: POŠILJANJE SMS NEUSPEŠNO</string>
<string name="sms_alarms_disabled">SMS alarmi Onemogočeni - ne javlja nič!</string>
<string name="phone_alarm_disabled">Alarmi telefona Onemogočeni</string>
<string name="test_phone_alarm_notification">Testni alarm telefona</string>
<string name="DiallerNotInstalledWarning"><a href="https://github.com/OpenSeizureDetector/Dialler/tree/master/app/release/app-release.apk">OpenSeizureDetector Dialer App</a> Ni inštaliran - Potreben je za opozorilne klice.</string>
<string name="title_devices">BLE Naprave</string>
<string name="ble_not_supported">BLE ni podprto</string>
<string name="error_bluetooth_not_supported">Bluetooth ni podprt</string>
<string name="menu_stop">STOP</string>
<string name="menu_scan">IŠČI</string>
<string name="unknown_device">Neznana naprava</string>
<string name="select_ble_device_title">Izberi BLE Napravo (samo za Bluetooth kot podatkovni vir)</string>
<string name="select_ble_device_desc">Izberi napravo Bluetooth Low Energy (BLE) za pridobitev podatkov o napadih (pospešek in srčni utrip) data) pri uporabi Bluetooth naprave kot podatkovnega vira.</string>
<string name="basic_settings_title">Osnovno</string>
<string name="basic_settings_summary">Osnovne Nastavitve</string>
<string name="general_settings_title">General</string>
<string name="general_settings_summary">Splošne preference</string>
<string name="alarms_settings_title">Alarmi</string>
<string name="alarms_settings_summary">Preference alarmov</string>
<string name="seizure_detector_settings_title">Detektor Napadov</string>
<string name="seizure_detector_settings_summary">Preference detektorja napadov</string>
<string name="pebble_datasource_summary">Preference podatkovnega vira Pebble</string>
<string name="network_datasource_title">Vir Podatkov Omrežje</string>
<string name="network_datasource_summary">Preference podatkovnega vira Omrežje</string>
<string name="pebble_datasource_title">Vir Podatkov Pebble</string>
<string name="select_datasource_title">Izberi Podatkovni Vir</string>
<string name="select_datasource_summary">Izberi želeni podatkovni vir detektorja napadov.</string>
<string name="log_alarms_summary">Logiranje alarmov v pomnilnik telefona</string>
<string name="log_alarms_title">Log Alarmnih Dogodkov</string>
<string name="log_data_summary">Logiranje podatkov v pomnilnik telefona</string>
<string name="log_data_title">Podatki Logiranja</string>
<string name="log_data_remote_summary">Deljenje podatkov z OpenSeizureDetector</string>
<string name="log_data_remote_title">Deljenje Podatkov</string>
<string name="log_data_remote_mobile_summary">Uporabi mobilni internet za delitev podatkov</string>
<string name="log_data_remote_mobile_title">Uporabi Mobilni Internet</string>
<string name="remote_uname_title">Uporabniško Ime</string>
<string name="remote_uname_summary">Uporabniško ime za deljenje podatkov</string>
<string name="remote_passwd_summary">Geslo za deljenje podatkov</string>
<string name="remote_passwd_title">Geslo</string>
<string name="wearer_id_summary">ID uporabnika</string>
<string name="wearer_id_title">ID uporabnika, ki uporablja pametno uro (iz OSD Web API)</string>
<string name="remote_url_summary">URL za oddaljeno logiranje podatkov.</string>
<string name="remote_url_title">Oddaljeni URL</string>
<string name="prevent_sleep_summary">Prepreči ugašanje zaslona med delovanjem aplikacije.</string>
<string name="prevent_sleep_title">Prepreči Ugašanje Zaslona</string>
<string name="data_update_period_summary">Prikaži periodo osveževanja v milisekundah.</string>
<string name="data_update_period_title">Prikaži periodo osveževanja (ms).</string>
<string name="auto_start_summary">Samodejno zaženi App ob zagonu naprave</string>
<string name="auto_start_title">Samodejno Zaženi App ob Zagonu</string>
<string name="app_version_summary">Verzija aplikacije - uporabljeno za odločanje o prikazu sporočila dobrodošlice.</string>
<string name="app_version_title">Verzija Aplikacije</string>
<string name="latch_alarms_summary">Require manual reset of alarms to reset them to silence them.</string>
<string name="latch_alarms_title">Trajanje Alarmov</string>
<string name="latch_timer_period_title">Trajanje Alarmov (sek)</string>
<string name="latch_timer_period_summary">Koliko sekund bodo alarmi opozarjali, preden se samodejno utišajo.</string>
<string name="enable_audible_alarm_summary">Sproži zvočni alarm, če datektor napadov zazna alarmno stanje.</string>
<string name="enable_audible_alarm_title">Omogoči Zvočni Alarm</string>
<string name="enable_audible_warning_summary">Sproži zvočni alarm, če datektor napadov zazna opozorilno (pred-alarmno) stanje.</string>
<string name="enable_audible_warning_title">Omogoči Zvočna Opozorila</string>
<string name="enable_audible_fault_summary">Sproži zvočni alarm, če sistem zazna napako (npr. ni komunikacije s pametno uro).</string>
<string name="enable_audible_fault_title">Omogoči Zvočna Opozorila Napak Sistema</string>
<string name="fault_timer_period_title">Časovnik Trajanja Napake (sek)</string>
<string name="fault_timer_period_summary">Trajanje zakasnitve alarmiranja o napakah sistema (preden se sprožijo).</string>
<string name="use_mp3_alarm_summary">Predvajaj MP3 datoteko za predvajanje opozorilnih alarmov (namesto privzetega generatorja tonov).</string>
<string name="use_mp3_alarm_title">Uporabi MP3 Zvok Alarma</string>
<string name="enable_sms_alarm_summary">Pošlji SMS alarm, če detektor napadov zazna alarmno stanje. OPOZORILO: SPOROČILO BO VSEBOVALO LOKACIJO UPORABNIKA. Onemogoči to izbiro, če deljenje lokacije vzbuja skrbi.</string>
<string name="enable_sms_alarm_title">Omogoči SMS Alarm</string>
<string name="sms_numbers_summary">Telefonske št. (1 ali več) za obveščanje z SMS Alarmi (ločene z vejico).</string>
<string name="sms_numbers_title">Telefonske št. SMS Alarmov</string>
<string name="sms_message_summary">SMS sporočilo za pošiljanje kadar je zaznan napad.</string>
<string name="sms_message_title">SMS Sporočilo</string>
<string name="server_ip_summary">IP naslov strežnika OpenSeizureDetector (npr. 192.168.1.175).</string>
<string name="server_ip_title">IP Naslov Strežnika.</string>
<string name="network_update_period_summary">Perioda med strežniškimi zahtevami v milisekundah.</string>
<string name="network_update_period_title">Perioda Posodobitve Podatkov (ms)</string>
<string name="connection_timeout_summary" />
<string name="connection_timeout_title">Timeout Povezovanja (ms)</string>
<string name="read_timeout_summary" />
<string name="read_timeout_title">Timeout Branja (ms)</string>
<string name="ble_device_settings_title">Nastavitve Naprave BLE</string>
<string name="ble_mac_addr_summary">MAC naslov naprave BLE kot podatkovnega vira</string>
<string name="ble_mac_addr_title">MAC Naslov Naprave</string>
<string name="ble_device_name_summary">Ime naprave BLE kot podatkovnega vira</string>
<string name="ble_device_name_title">Ime Naprave</string>
<string name="user_interface_settings_title">Nastavitve Uporabniškega Vmesnika</string>
<string name="pebble_update_period_summary">Časovna perioda pošiljanja podatkov telefonu (ne vpliva na frekvenco analize - tisto je v nastavitvi Perioda Vzorčenja)</string>
<string name="mute_period_summary">Trajanje utišanja alarmov po dolgem pritisku gumba GOR</string>
<string name="pebble_update_period_title">Perioda Prenosa Podatkov (sek)</string>
<string name="mute_period_title">Perioda Utišanja (sek)</string>
<string name="manual_alarm_period_summary">Trajanje zvonjenja ročnih alarmov po dolgem pritisku gumba DOL</string>
<string name="manual_alarm_period_title">Perioda Ročnega Alarma (sek)</string>
<string name="warn_time_summary">Trajanje pred začetkom opozarjanja (privzeto = 5 sek)</string>
<string name="warn_time_title">Čas do Opozarjanja (sek)</string>
<string name="alarm_time_summary">Trajanje pred začetkom alarmiranja (privzeto = 10 sek)</string>
<string name="alarm_time_title">Čas do Alarmiranja (sek)</string>
<string name="alarm_threshold_summary">Prag za proženje Alarma (privzeto = 100)</string>
<string name="alarm_threshold_title">Prag Alarma</string>
<string name="alarm_ratio_thresh_summary">Prag razmerja alarma (privzeto = 50). Povečaj to vrednost za zmanjšanje občutljivosti, če so lažni alarmi problem.</string>
<string name="alarm_ratio_thresh_title">Prag Razmerja Alarma</string>
<string name="alarm_freq_min_summary">Minimum za Območje Zanimanja, t.j. ROI (privzeto = 3 Hz)</string>
<string name="alarm_freq_min_title">Min. Frekvenca Alarma (Hz)</string>
<string name="alarm_freq_max_summary">Maksimum za Območje Zanimanja, t.j. ROI (privzeto = 10 Hz)</string>
<string name="alarm_freq_max_title">Max Frekvenca Alarma (Hz)</string>
<string name="sample_period_summary">Perioda med analizami podatkov (v sekundah)</string>
<string name="sample_period_title">Perioda Vzorčenja (sek)</string>
<string name="seizure_detect_mode_summary">Izberi enega od treh načinov delovanja.</string>
<string name="seizure_detect_mode_title">Način Detekcije Napadov</string>
<string name="sample_freq_summary">Višja frekvenca je bolj natančna, ampak porabi več baterije.</string>
<string name="sample_freq_title">Izberi Frekvenco Vzorca</string>
<string name="fall_detect_title">Nastavitve Detektorja Padcev</string>
<string name="fall_detect_active_title">Vključi Funkcijo Detekcije Padcev</string>
<string name="fall_thresh_min_title">Spodnji Prag Detekcije Padcev (milli-g)</string>
<string name="fall_thresh_max_title">Zgornji Prag Detekcije Padcev (milli-g)</string>
<string name="fall_window_title">Časovno Okno Detekcije Padcev (mili sekund)</string>
<string name="watch_comms_title">Nastavitve Komunikacije Ure</string>
<string name="debug_mode_summary">Vključi ali Izključi odkrivanje napak.</string>
<string name="debug_mode_title">Odkrivanje Napak Detektorja Napadov</string>
<string name="app_restart_timeout_summary">Perioda čakanja na podatke iz ure preden predpostavimo, da ne dela, in ponovno zaženemo aplikacijo na uri.</string>
<string name="app_restart_timeout_title">Perioda čakanja podatkov (v sekundah) pred ponovnim zagonom aplikacije na uri.</string>
<string name="display_spectrum_mode_title">Select Display Spectrum Mode</string> <!-- what is this -->
<string name="display_spectrum_mode_summary">Vključi ali Izključi Display Spectrum.</string> <!-- what is this -->
<string name="analysis_prefs_title">Analiza</string>
<string name="watch_comms_settings_title">Nastavitve Komunikacije z Uro</string>
<string name="ParsePreferenceWarning">Problem Parsing Preferences - Something won\'t work - Please go back to Settings and correct it!</string> <!-- same as line 135? -->
<string name="ErrorWritingLogFileWarning">NAPAKA pisanja v datoteko Logiranja</string>
<string name="RestartingServerMsg">Dovoljenja spremenjena - ponovno zaženi strežnik</string>
<string name="ErrorReleasingWakelockMsg">Napaka pri sprostitvi Wakelock mehanizma</string>
<string name="SMSAlarmAlreadySentMsg">SMS Alarm že poslan - ni ponovnega pošiljanja</string>
<string name="SMSAlarmDisabledNotSendingMsg">SMS Alarmi Onemogočeni - SMS opozorila se ne pošiljajo</string>
<string name="PleaseForceStopOSDorRebootMsg">NAPAKA - OpenSizureDetector se ni pravilno zagnal - prosim Prisilno zaustavi OpenSeizureDetector ali ponovno zaženi telefon.</string>
<string name="SMSPermissionsDeniedMsg">NAPAKA - Dovoljenje za SMS ali Lokacijo zavrnjeno - SMS se ne pošiljajo</string>
<string name="DatasourceTitle">\"Podatkovni vir\"</string>
<string name="DefaultingToPhoneMsg">\" ni prepoznan - Privzeto nastavljen na Telefon\"</string>
<string name="SMSAlarmCancelledMsg">SMS Alarm Preklican</string>
<string name="StopServerTitle">Ustavi Strežnik</string>
<string name="StartServerTitle">Startaj Strežnik</string>
<string name="StartingTitle">Starting......</string>
<string name="AcceptAlarmBtnTxt">Sprejmi Alarm</string>
<string name="CancelAudibleButtonTxt">Prekliči Zvoke (začasno)</string>
<string name="DataLoggingSettingsTitle">Nastavitve Logiranja podatkov</string>
<string name="HeartRateAlarmSettingsTitle">Nastavitve Alarmov Srčnega Utripa</string>
<string name="AlarmFunctionalitySettingsTitle">Nastavitve Alarmov</string>
<string name="AudibleAlarmSettingsTitle">Nastavitve Zvočnih Alarmov</string>
<string name="SMSAlarmSettingsTitle">Nastavitve SMS Alarmov</string>
<string name="DefaultSMSMsgText">**NAPAD ZAZNAN**</string>
<string name="AlarmLoggingTitle">Logiranje Alarmov</string>
<string name="WarnTimeSummary">Perioda čakanja preden se prožijo opozorila (privzeto = 5 sek)</string> <!-- lines 283..286 already in 228...231 -->
<string name="WarnTimeTitle">Zamik Opozoril (sek)</string>
<string name="AlarmTimeSummary">Perioda čakanja preden se prožijo alarmi (privzeto = 10 sek)</string>
<string name="AlarmTimeTitle">Zamik Alarmov (sek)</string>
<string name="O2SatSettingsTitle">Nastavitve Alarmov Sauturacije Kisika v Krvi</string>
<string name="O2Sat_enabled_summary">O2Sat_enabled_summary</string> <!-- Missing "human string"? -->
<string name="O2Sat_enabled_title">Omogoči Alarme Saturacije Kisika</string>
<string name="O2SatNullAlarmSummary">Obravnavaj napako (stanje brez meritev saturacije) kot alarm</string>
<string name="O2SatNullAlarmTitle">Obravnavaj stanje brez Saturacije kot alarm</string>
<string name="O2SatThreshMinTitle">Min. Saturacije kisika za Alarm (%)</string>
<string name="O2SatThreshMinSummary">Spodnja meja Saturacije kisika za proženje Alarmov (%)</string>
<string name="title_activity_authenticate">Prijava v Deljenje Podatkov OpenSeizureDetector</string>
<string name="logout">Odjava</string>
<string name="logged_in_with_token">Prijavljen v\nDeljenje Podatkov</string>
<string name="local_database">Upravitelj Podatkov Logiranja</string>
<string name="remote_database">Deljeni Podatki</string>
<string name="num_local_events">Št. Dogodkov Shranjenih na Telefonu: </string>
<string name="num_local_datapoints">"Št. Podatkovnih Točk Shranjenih na Telefonu: "</string>
<string name="view_remote_db_btn">Ogled Podatkov Oddaljene Baze</string>
<string name="report_seizure">Prijavi Napad</string>
<string name="date">"Datum: "</string>
<string name="select_date">Izberi Datum</string>
<string name="time">"Čas: "</string>
<string name="cancelBtnTxt">Preklic</string>
<string name="select_time">Izberi Čas</string>
<string name="EventsInLocalDb">Dogodki Shranjeni na Telefonu</string>
<string name="createdNewEvent">Ustvarjen nov Ročni Alarm</string>
<string name="DatapointNotFound">Podatkovna točka ni najdena - nič ni zabeleženo</string>
<string name="logging_settings_title">Nastavitve Logiranja podatkov</string>
<string name="logging_settings_summary">Nastavitve za upravljanje, kako so podatki shranjeni na telefonu in naloženi v Podatkovno bazo OpenSeizure</string>
<string name="eventDurationSummary">Trajanje (v sekundah) pred in po napadu, za katerega se beležijo podatki.</string>
<string name="eventDurationTitle">Trajanje Dogodka (v sekundah)</string>
<string name="dataRetentionPeriodTitle">Obdobje Hranjenja Podatkov (v dneh)</string>
<string name="dataRetentionPeriodSummary">Št. dni za katere se hrani podatke in so zaščiteni pred možnostjo \'Oklesti Bazo podatkov\' Option.</string>
<string name="AutoPruneDbTitle">Samodejno Oklesti Bazo podatkov</string>
<string name="AutoPruneDbSummary">Samodejno periodično Oklesti Bazo podatkov, da se prepreči pretiranjo zasedanje pomnilniškega prostora (spomina).</string>
<string name="remoteLogPeriodSummary">Perioda (v sekundah) med poizkusi nalaganja podatkov na oddljeni strežnik. Vsak poizkus naloži samo en dogodek, ne vseh rapoložljivih podatkov.</string>
<string name="remoteLogPeriodTitle">Perioda Logiranja Podatkov (v sekundah)</string>
<string name="ManualAlarmBtnTxt">Sproži Alarm</string>
<string name="save">Shrani</string>
<string name="event_type">Tip dogodka:</string>
<string name="event_sub_type">"Pod-tip dogodka: "</string>
<string name="selectFromOptionselow">"-- izberi možnost --"</string>
<string name="waitingForData">...čakanje na podatke...</string>
<string name="refreshBtn">Osveži</string>
<string name="back">Nazaj</string>
<string name="unvalidatedEventsTitle">Nepotrjeni Dogodki Napadov</string>
<string name="register">Registriraj Novega Uporabnika</string>
<string name="reset_password">Ponastavi Geslo</string>
<string name="login_to_osdapi">Prijava za Deljenje Podatkov</string>
<string name="login">Prijava</string>
<string name="data_sharing_status">Deljenje Podatkov</string>
<string name="not_logging_data">Ni deljenja Podatkov</string>
<string name="not_sharing_logged_data">Ni deljenja Logiranih Podatkov</string>
<string name="not_logged_in">Neprijavljen</string>
<string name="data_sharing_setup_ok">Nastavitev Deljenja Podatkov je OK</string>
<string name="please_confirm_seizure_events">Prosim preveri svoje Deljene dogodke Napadov</string>
<string name="data_sharing_log_in">Prijava za Deljenje Podatkov</string>
<string name="not_updating_mobile">Ni uporabe Mobilnega Interneta</string>
<string name="not_updating_no_network">Ni Podatkovne Povezave</string>
<string name="error_connecting_to_server">Napaka Povezovanje na Strežnik</string>
<string name="battery_usage_optimisation_dialog_title">OPOZORILO: Optimizacija Rabe Baterije</string>
<string name="battery_usage_optimisation_dialog_text">OPOZORILO: Android sistemske Nastavitve Optimizacija Baterije so nastavljene na \'Optimiraj\' OpenSeizureDetector. To pomeni, da bo aplikacija najverjetneje prisilno ustavljena, kadar naprava ni priklopljena na polnjenje, in ne bo delovala. \n\nProsim, da v Nastavitvah Telefona poiščete \'Optimizacija Baterije\' in nastavite, da OpenSeizureDetector NI optimiziran</string>
<string name="local_data">Lokalni podatki</string>
<string name="shared_data">Deljeni podatki</string>
<string name="prune_database">Oklesti bazo podatkov</string>
<string name="check_seizures_message">Prosim izberi dogodke označene z roza za določanje, ali so to pravi napadi ali lažni alarmi</string>
<string name="error_server_not_running">NAPAKA: OpenSeizureDetector Strežnik ne dela - prosim ponovno ga zaženi</string>
</resources>

View File

@@ -119,7 +119,6 @@
<string name="sms_location_alarm_active">SMS positionslarm PÅ</string>
<string name="sms_location_alarm_disabled">SMS positionslarm AV</string>
<string name="cancel_audible_not_sending_sms">Stäng av ljudlarm - skicka inte SMS</string>
<string name="sms_alarm_disabled">SMS-larm avstängt- ingen åtgärd</string>
<string name="no_wifi_connection">Nätverksstatus ändrad - ingen uppkoppling</string>
<string name="no_active_network">Nätverksstatus ändrad - inga aktiva nätverk</string>
<string name="problem_parsing_preferences">Något gick fel! Problem med inställningarna - justera dessa.</string>

View File

@@ -3,6 +3,4 @@
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="fragment_horizontal_margin">16dp</dimen>
<dimen name="fragment_vertical_margin">16dp</dimen>
</resources>

View File

@@ -2,35 +2,41 @@
<resources>
<string name="app_name">OpenSeizureDetector</string>
<string name="changelog">
"V3.6.2 - Fix of issue with log file permissions on some Android 10 devices and added more translatable strings with polish translation.
\nV3.6.1 - Possible fix for issue with shutting down system and expanded Polish translation to all settings screens.
\nV3.6 - Added phone sensor data source for testing without a watches
\nV3.5 - Added support for SMS Annunciator App
\nV3.4 - Added support for BLE Data Source "</string>
"\n
\nV4.0.5 - fixed issue with O2 saturation data not being recorded to data sharing database.
\nV4.0.4
- Introduced the &lt;b>Data Sharing&lt;/b> feature to allow users to share their seizure and false alarm data
with researchers to help improve the system.&lt;br/>
- Fixed &lt;b>SMS Location Alerts&lt;/b> on Android V10+ (new permissions screens)&lt;br/>
- Added explicit link to the &lt;b>Privacy Policy&lt;/b> &lt;br/>
"</string>
<string name="UpgradeMsg">
OpenSeizureDetector does not collect any personal data.
This does mean that it is not possible for me to contact users if I find an
issue with the app that you should be aware of. \nPlease subscribe to updates at
http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector.
so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk
\n\nChanges in this version:
\n
Please enable the new &lt;b>Data Sharing&lt;/b> feature to help improve OpenSeizureDetector!&lt;br/>
(see below for details)&lt;br/>&lt;br/>
Please subscribe to updates at &lt;b>www.openseizuredetector.org.uk&lt;/b>
so we can inform you of any issues.
&lt;br/>&lt;br/>
&lt;b>Changes in this version:&lt;/b>&lt;br/>
</string>
<string name="FirstRunDlgMsg">
OpenSeizureDetector does not collect any personal data.
This does mean that it is not possible for me to contact users if I find an
issue with the app that you should be aware of. \nPlease subscribe to updates at
http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector.
so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk
\n\nChanges in this version:
For details of how OpenSeizure Detector collects and uses
your personal data, please refer to the &lt;b>Privacy Policy below&lt;/b>.&lt;br/>&lt;br/>
Please enable &lt;b>Data Sharing&lt;/b> to help improve OpenSeizureDetector!&lt;br/>
(see below for details)&lt;br/>&lt;br/>
Please subscribe to updates at &lt;b>www.openseizuredetector.org.uk&lt;/b>
so we can inform you of any issues.
</string>
<string name="ask_for_error_log">Sorry, OpenSeizureDetector Has Crashed. Please Email this log file to us so we can work out what happened and fix it.\nThanks, Graham.</string>
<string name="email_welcome_note">Dear OpenSeizureDetector,\n\nApplication is just crashed, please check following error log for more details.\n\n\n</string>
<string name="copyright_info">OpenSeizureDetector (Using UCE Handler\nCopyright © 2018 Rohit Sahebrao Surwase.)</string>
<string name="okBtnTxt">OK</string>
<string name="closeBtnTxt">Close</string>
<string name="UpdateDialogTitleTxt">Thank you for Updating OpenSeizureDetector</string>
<string name="FirstRunDlgTitle">Welcome to OpenSeizureDetector</string>
<string name="SmsPermissionWarning">Problem with SMS Permissions</string>
<string name="SmsPermissionWarning">Problem with SMS Permissions\n(please check phone settings -> Apps -> OpenSeizureDetector -> Permissions</string>
<string name="AppPermissionsOk">App Permissions OK</string>
<string name="AppPermissionsWarning">Problem with App Permissions</string>
<string name="BoundToServiceOk">Bound to Service OK</string>
@@ -64,13 +70,13 @@
<string name="SMSWillBeSentIn">SMS Will Be Sent in </string>
<string name="Cancel">CANCEL?</string>
<string name="AcceptAlarm">Accept Alarm</string>
<string name="AudibleAlarmsCancelledFor">Audible Alarms Cancelled for </string>
<string name="PressToReEnable"> Press to re-enable</string>
<string name="CancelAudibleAlarms">Cancel Audible Alarms (temporarily)</string>
<string name="AudibleAlarmsCancelledFor">Muted for </string>
<string name="PressToReEnable"> Restore</string>
<string name="CancelAudibleAlarms">Mute Alarms</string>
<string name="AudibleAlarmsOff">Audible Alarms OFF</string>
<string name="Fault">FAULT</string>
<string name="authenticate">Authenticate</string>
<string name="not_authenticated">Not Authenticated</string>
<string name="authenticate">Log In</string>
<string name="not_authenticated">Not Logged In</string>
<string name="cancel">Cancel</string>
<!-- Strings related to login -->
<string name="prompt_email">Email</string>
@@ -121,13 +127,27 @@
<string name="export_data">Export Data</string>
<string name="settings">Settings</string>
<string name="about">About...</string>
<string name="about_text">OpenSeizureDetector epileptic\n (tonic-clonic) seizure detector and \n alarm system. Uses a Pebble Smart\n watch to detect the shaking \n associated with a seizure, \n then raises audible and text\n message (SMS) alerts for carers.\n The system is free and open source - see \n http://openseizuredetector.org.uk for details. Please report any issues to graham@openseizuredetector.org.uk\n or raise an issue on the project github source code \n repository - https://github.com/OpenSeizureDetector</string>
<string name="credits_text">Main Watch App and Android App \n copyright Graham Jones, 2015.\n The following libraries are used:\n - SYLT-FFT - https://github.com/stg/SYLT-FFT by D. Taylor.\n - NanoHTTPD - https://github.com/NanoHttpd/nanohttpd\n - jQuery - http://jquery.org\n - jBeep - http://www.ultraduz.com.br\n - Chartjs - http://www.chartjs.org\n - MPAndroidChart - https://github.com/PhilJay/MPAndroidChart\n - UCE-Handler - https://github.com/RohitSurwase/UCE-Handler\n \n The Logo is based on Star of life2 by Verdy P, \n Licensed under Public Domain via\n Wikimedia Commons (http://commons.wikimedia.org/wiki/File:Star_of_life2.svg#mediaviewer/File:Star_of_life2.svg).</string>
<string name="about_text">OpenSeizureDetector epileptic\n
(tonic-clonic) seizure detector and alarm
system. Uses a Smart
watch to detect the shaking
associated with a seizure,
then raises audible and text
message (SMS) location alerts for carers.\n\n
The system is free and open source - see
openseizuredetector.org.uk for details.\n\n
Please report any issues by raising an issue on the source code repository - https://github.com/OpenSeizureDetector/Android_Pebble_SD/issues
or email graham@openseizuredetector.org.uk\n\n
Android App and Garmin Watch App,
copyright Graham Jones, 2015-2022.\n
</string>
<string name="credits_text"></string>
<string name="edit_settings">Edit Settings</string>
<string name="sms_location_alarm_active">SMS Location Alarm Active</string>
<string name="sms_location_alarm_disabled">SMS Location Alarm Disabled</string>
<string name="cancel_audible_not_sending_sms">Cancel Audible Active - not sending SMS</string>
<string name="sms_alarm_disabled">SMS Alarms Disabled - not doing anything!</string>
<!--<string name="sms_alarm_disabled">SMS Alarms Disabled - not doing anything!</string>-->
<string name="no_wifi_connection">Network State Changed - no Wifi Connection</string>
<string name="no_active_network">Network State Changed - No Active Network</string>
<string name="problem_parsing_preferences">Problem Parsing Preferences - Something won\'t work - Please go back to Settings and correct it!</string>
@@ -159,18 +179,18 @@
<string name="pebble_datasource_title">Pebble Datasource</string>
<string name="select_datasource_title">Select Data Source</string>
<string name="select_datasource_summary">Select the seizure detector data source to use.</string>
<string name="log_alarms_summary">Log Alarm events to SD Card</string>
<string name="log_alarms_title">Log Alarm events to SD Card</string>
<string name="log_data_summary">Log Data to SD Card Regularly</string>
<string name="log_data_title">Log Data to SD Card</string>
<string name="log_data_remote_summary">Log Data to Central OpenSeizureDetector Database</string>
<string name="log_data_remote_title">Log Data Remotely</string>
<string name="log_data_remote_mobile_summary">Use mobile internet to log remote data</string>
<string name="log_alarms_summary">Log Alarm events to Phone Storage</string>
<string name="log_alarms_title">Log Alarm Events</string>
<string name="log_data_summary">Log Data to Phone Storage</string>
<string name="log_data_title">Log Data</string>
<string name="log_data_remote_summary">Share Data with OpenSeizureDetector</string>
<string name="log_data_remote_title">Share Data</string>
<string name="log_data_remote_mobile_summary">Use mobile internet to share data</string>
<string name="log_data_remote_mobile_title">Use Mobile Internet</string>
<string name="remote_uname_title">Username for remote data logging.</string>
<string name="remote_uname_summary">Remote Username</string>
<string name="remote_passwd_summary">Password for remote data logging.</string>
<string name="remote_passwd_title">Remote Password</string>
<string name="remote_uname_title">Username</string>
<string name="remote_uname_summary">Username for data sharing</string>
<string name="remote_passwd_summary">Password for data sharing</string>
<string name="remote_passwd_title">Password</string>
<string name="wearer_id_summary">Wearer ID</string>
<string name="wearer_id_title">Wearer ID of the person wearing the watch (from OSD Web API)</string>
<string name="remote_url_summary">URL for remote data logging.</string>
@@ -191,7 +211,7 @@
<string name="enable_audible_alarm_title">Enable Audible Alarm</string>
<string name="enable_audible_warning_summary">Issue an audible alarm if the seizure detector enters a warning (pre-alarm) condition.</string>
<string name="enable_audible_warning_title">Enable Audible Warnings</string>
<string name="enable_audible_fault_summary">Issue an audible alarm if the system detects a fault (e.g. can not talk to Pebble).</string>
<string name="enable_audible_fault_summary">Issue an audible alarm if the system detects a fault (e.g. can not talk to watch).</string>
<string name="enable_audible_fault_title">Enable Audible System FaultWarnings</string>
<string name="fault_timer_period_title">Fault Timer Duration (sec)</string>
<string name="fault_timer_period_summary">Duration that fault alarms are muted before initiating.</string>
@@ -282,4 +302,139 @@
<string name="WarnTimeTitle">WarnTime (sec)</string>
<string name="AlarmTimeSummary">Time to wait before initiating alarm (Default = 10 sec)</string>
<string name="AlarmTimeTitle">AlarmTime (sec)</string>
<string name="O2SatSettingsTitle">Blood Oxygen Saturation Alarm Settigs</string>
<string name="O2Sat_enabled_summary">O2Sat_enabled_summary</string>
<string name="O2Sat_enabled_title">Enable O2 Saturation Alarm</string>
<string name="O2SatNullAlarmSummary">Treat an error condition (null value of oxygen saturation reading) as an alarm condition</string>
<string name="O2SatNullAlarmTitle">Treat Null Value as Alarm</string>
<string name="O2SatThreshMinTitle">Oxygen Saturation Low Alarm Level (%)</string>
<string name="O2SatThreshMinSummary">O2 Saturation Low Alarm Level (%)</string>
<string name="title_activity_authenticate">Log in to OpenSeizureDetector Data Sharing</string>
<string name="logout">Log Out</string>
<string name="logged_in_with_token">Logged in to\nData Sharing</string>
<string name="local_database">Logged Data Manager</string>
<string name="remote_database">Shared Data</string>
<string name="num_local_events">Number of Events Stored on Phone: </string>
<string name="num_local_datapoints">"Number of Datapoints Stored on Phone: "</string>
<string name="view_remote_db_btn">View Remote DB Data</string>
<string name="report_seizure">Report Seizure</string>
<string name="date">"Date: "</string>
<string name="select_date">Select Date</string>
<string name="time">"Time: "</string>
<string name="cancelBtnTxt">Cancel</string>
<string name="select_time">Select Time</string>
<string name="EventsInLocalDb">Events Stored on Phone</string>
<string name="createdNewEvent">Created new Manual Alarm Event</string>
<string name="DatapointNotFound">Datapoint not found - not doing anything</string>
<string name="logging_settings_title">Data Logging Settings</string>
<string name="logging_settings_summary">Settings that control how data is recorded on the phone and uploaded to the Open Seizure Database</string>
<string name="eventDurationSummary">The time (in seconds) before and after a seizure event that we record data.</string>
<string name="eventDurationTitle">Event Duration (seconds)</string>
<string name="dataRetentionPeriodTitle">Data Retention Period (days)</string>
<string name="dataRetentionPeriodSummary">The period (in days) that data will be retained and is protected from deletion by the \'Prune Database\' Option.</string>
<string name="AutoPruneDbTitle">Automatically Prune (Trim) Database</string>
<string name="AutoPruneDbSummary">Automatically Prune (Trim) the Database periodically to prevent excessive storage capacity (memory) usage.</string>
<string name="remoteLogPeriodSummary">The period (in seconds) between attempts to upload data to the remote server. Each attempt only uploads a single event, not all the available data.</string>
<string name="remoteLogPeriodTitle">Remote Log Period (seconds)</string>
<string name="ManualAlarmBtnTxt">Raise Alarm</string>
<string name="save">Save</string>
<string name="event_type">Event Type:</string>
<string name="event_sub_type">"Event Sub-Type: "</string>
<string name="selectFromOptionselow">"-- select option --"</string>
<string name="waitingForData">...waiting for data...</string>
<string name="refreshBtn">Refresh</string>
<string name="back">Back</string>
<string name="unvalidatedEventsTitle">Un-Confirmed Seizure Events</string>
<string name="register">Register New User</string>
<string name="reset_password">Reset Password</string>
<string name="login_to_osdapi">Log in to Share Data</string>
<string name="login">Log In</string>
<string name="data_sharing_status">Data Sharing</string>
<string name="not_logging_data">Not Logging Data</string>
<string name="not_sharing_logged_data">Not Sharing Logged Data</string>
<string name="not_logged_in">Not Logged In</string>
<string name="data_sharing_setup_ok">Data Sharing Setup OK</string>
<string name="please_confirm_seizure_events">Please Check your Shared Seizure Events</string>
<string name="data_sharing_log_in">Data Sharing Log-In</string>
<string name="not_updating_mobile">Not Updating using Mobile Internet</string>
<string name="not_updating_no_network">No Network Connection</string>
<string name="error_connecting_to_server">Error Connecting to Server</string>
<string name="battery_usage_optimisation_dialog_title">WARNING: Battery Usage Optimisation</string>
<string name="battery_usage_optimisation_dialog_text">
<b>WARNING: The Android System Battery Optimisation Settings are configured to \'Optimise\' OpenSeizureDetector.</b>\n\n
This means it is likely to be shutdown when operating on battery power, so will not work correctly\n\n
Please go into the Phone Settings and select <b>Apps->OpenSeizureDetector->Battery->Optimise battery usage</b> and set it so that OpenSeizureDetector is NOT optimised</string>
<string name="local_data">Local Data</string>
<string name="shared_data">Shared Data</string>
<string name="prune_database">Prune Database</string>
<string name="check_seizures_message">Please select the events highlighted in pink to say if they are real seizures or false alarms</string>
<string name="error_server_not_running">ERROR: OpenSeizureDetector Server is not running - please re-start it</string>
<string name="system_logs">System Logs</string>
<string name="logged_in_as_user_id">Logged in as: </string>
<string name="datasharing_notification_text">Select for more information</string>
<string name="datasharing_notification_title">OpenSeizureDetector Data Sharing Problem</string>
<string name="datasharing_about_title">OpenSeizureDetector Data Sharing</string>
<string name="datasharing_about_text">
Data Sharing is not working correctly. \n
This might be because you have not registered an account and logged in to the data sharing system,\n
or it may be a networking problem. \n\n
<b>Please register for Data Sharing and Log in using the App menu or button below</b>\n\n
This will help with developing OpenSeizureDetector to increase the
detection reliability and reduce the false alarm rate\n\n
If you would like more information about the data sharing system and privacy policy, please see the
Data Sharing page (https://www.openseizuredetector.org.uk/?page_id=1818)
\n on the
https://openseizuredetector.org.uk web site.
</string>
<string name="sms_permissions_rationale_1">
OpenSeizureDetector needs permission to <b>read your phone state and send SMS messages</b> to enable it to send SMS alerts when it detects a seizure.\n\n
With this permission enabled the App will <b>send an SMS message to the phone numbers you enter on th Alarm settings page of the app when it detects a seizure</b>.\n\n
Please grant the permissions in the following screens after pressing \'OK\' below....\n\n
You can change this later by going into the phone settings and selecting Apps->OpenSeizureDetector->Permissions
</string>
<string name="location_permissions_rationale_1">
OpenSeizureDetector <b>collects location data</b>
to enable the <b>SMS Location Alarms</b> feature
to include your location in the alarm message
<b>when the app is closed or not in use</b>.\n\n
This means that it will be able to send <b>SMS text messages</b> containing
<b>your location</b>
to the <b>phone numbers you enter</b> on the Alarm settings page of the app
when it detects a seizure, by running in the <b>background</b>,
even <b>when the app is closed</b>.\n
It does not store your location or share it with anyone other than the phone numbers
you specify\n\n
Please grant the permissions in the following screen by selecting
<b>While using the app</b>
if you wish to use SMS Location Alarms\n\n
You can change this later by going into the phone settings and selecting
<b>Apps->OpenSeizureDetector->Permissions</b>.
</string>
<string name="location_permissions_2_rationale">
OpenSeizureDetector <b>collects location data</b>
to enable the <b>SMS Location Alarms</b> feature
to include your location in the alarm message
<b>when the app is closed or not in use</b>.\n\n
This means that it will be able to send <b>SMS text messages</b> containing
<b>your location</b>
to the <b>phone numbers you enter</b> on the Alarm settings page of the app
when it detects a seizure, by running in the <b>background</b>,
even <b>when the app is closed</b>.\n
It does not store your location or share it with anyone other than the phone numbers
you specify.\n\n
Please grant the required permission by selecting <b>Allow all the time</b>
on the next screen, if you wish to use SMS Location Alarms\n\n
You can change this later by going into the phone settings
and selecting <b>Apps->OpenSeizureDetector->Permissions</b>.
</string>
<string name="permissions_required">Permissions Disclosure</string>
<string name="about_data_sharing">About Data Sharing</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="mark_unverified_events_as_unknown">Mark Unverified Events as Unknown</string>
<string name="mark_unverified_events_unknown_dialog_title">Mark All Unverified Events as Unknown?</string>
<string name="mark_unverified_events_unknown_dialog_message">Please confirm that all genuine seizure events have been verified before marking all unverified events as type \'unknown\'. \n\nContinue to mark unverified events as Unknown?</string>
<string name="not_logged_in_dialog_title">Not Logged in to Data Sharing</string>
<string name="not_logged_in_dialog_message">You must be logged in to the Data Sharing system to be able to report seizures.</string>
</resources>

View File

@@ -7,11 +7,5 @@
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>

View File

@@ -70,6 +70,7 @@
android:title="Enable Phone Call Alarm" />
</PreferenceCategory>
-->
<!--
<PreferenceCategory android:title="@string/AlarmLoggingTitle">
<CheckBoxPreference
android:defaultValue="true"
@@ -82,4 +83,5 @@
android:summary="@string/log_data_summary"
android:title="@string/log_data_title"/>
</PreferenceCategory>
-->
</PreferenceScreen>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:defaultValue="false"
android:defaultValue="true"
android:enabled="true"
android:key="advancedMode"
android:summary="@string/AdvancedModeSummary"

View File

@@ -6,14 +6,9 @@
android:summary="@string/select_datasource_summary"
android:entries="@array/datasource_list"
android:entryValues="@array/datasource_list_values"
android:defaultValue="Garmin"
android:defaultValue="Phone"
android:dialogTitle="@string/select_datasource_title" />
<Preference
android:key="SelectBLEDevice"
android:title="@string/select_ble_device_title"
android:summary="@string/select_ble_device_desc"
android:widgetLayout="@layout/pref_select_ble_device_button"
/>
<!--
<CheckBoxPreference
android:defaultValue="true"
android:key="LogAlarms"
@@ -55,16 +50,18 @@
android:key="OSDUrl"
android:summary="@string/remote_url_summary"
android:title="@string/remote_url_title" />
-->
<CheckBoxPreference
android:defaultValue="false"
android:key="PreventSleep"
android:summary="@string/prevent_sleep_summary"
android:title="@string/prevent_sleep_title" />
<EditTextPreference
<!--<EditTextPreference
android:defaultValue="1000"
android:key="UpdatePeriod"
android:summary="@string/data_update_period_summary"
android:title="@string/data_update_period_title" />
android:title="@string/data_update_period_title" />-->
<CheckBoxPreference
android:defaultValue="false"
android:key="AutoStart"
@@ -72,6 +69,12 @@
android:title="@string/auto_start_title"
android:enabled="true"
/>
<Preference
android:key="SelectBLEDevice"
android:title="@string/select_ble_device_title"
android:summary="@string/select_ble_device_desc"
android:widgetLayout="@layout/pref_select_ble_device_button"
/>
<EditTextPreference
android:defaultValue=""

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!--<CheckBoxPreference
android:defaultValue="true"
android:key="LogAlarms"
android:summary="@string/log_alarms_summary"
android:title="@string/log_alarms_title" />
<CheckBoxPreference
android:defaultValue="true"
android:key="LogData"
android:summary="@string/log_data_summary"
android:title="@string/log_data_title" /> -->
<EditTextPreference
android:defaultValue="150"
android:key="EventDurationSec"
android:summary="@string/eventDurationSummary"
android:title="@string/eventDurationTitle" />
<CheckBoxPreference
android:defaultValue="true"
android:key="AutoPruneDb"
android:summary="@string/AutoPruneDbSummary"
android:title="@string/AutoPruneDbTitle" />
<EditTextPreference
android:defaultValue="28"
android:key="DataRetentionPeriod"
android:summary="@string/dataRetentionPeriodSummary"
android:title="@string/dataRetentionPeriodTitle" />
<!--<CheckBoxPreference
android:defaultValue="true"
android:key="LogDataRemote"
android:summary="@string/log_data_remote_summary"
android:title="@string/log_data_remote_title" /> -->
<CheckBoxPreference
android:defaultValue="false"
android:key="LogDataRemoteMobile"
android:summary="@string/log_data_remote_mobile_summary"
android:title="@string/log_data_remote_mobile_title" />
<!--<EditTextPreference
android:defaultValue="60"
android:key="RemoteLogPeriod"
android:summary="@string/remoteLogPeriodSummary"
android:title="@string/remoteLogPeriodTitle" />-->
<!--<EditTextPreference
android:defaultValue="https://osdapi.ddns.net/"
android:key="OSDUrl"
android:summary="@string/remote_url_summary"
android:title="@string/remote_url_title" /> -->
</PreferenceScreen>

View File

@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:defaultValue="https://osd.dynu.net/"
android:key="serverUrl"
android:title="@string/remote_url_title"
android:summary="@string/remote_url_summary"
/>
<CheckBoxPreference
android:defaultValue="true"
android:key="LogToServer"
android:summary="@string/log_data_remote_summary"
android:title="@string/log_data_remote_title" />
<CheckBoxPreference
android:defaultValue="false"
android:key="UploadOverMobileNetwork"
android:summary="@string/UseMobileSummary"
android:title="@string/UseMobileTitle" />
<EditTextPreference
android:defaultValue="password"
android:key="OSDPasswd"
android:summary="@string/remote_passwd_summary"
android:title="@string/remote_passwd_title" />
<CheckBoxPreference
android:defaultValue="false"
android:key="PreventSleep"
android:summary="@string/prevent_sleep_summary"
android:title="@string/prevent_sleep_title" />
<EditTextPreference
android:defaultValue="1000"
android:key="UpdatePeriod"
android:summary="@string/data_update_period_summary"
android:title="@string/data_update_period_title" />
<CheckBoxPreference
android:defaultValue="false"
android:key="AutoStart"
android:summary="@string/auto_start_summary"
android:title="@string/auto_start_title"
android:enabled="true"
/>
<EditTextPreference
android:defaultValue=""
android:key="AppVersionName"
android:summary="@string/app_version_summary"
android:title="@string/app_version_title" />
<!--
<CheckBoxPreference
android:defaultValue="false"
android:key="UseIpCamera"
android:summary="Use IP Camera to View Images"
android:title="Enable IP Camera"
android:enabled="false"
/>
-->
</PreferenceScreen>

View File

@@ -2,10 +2,10 @@
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="uk.org.openseizuredetector.PrefActivity$BasicPrefsFragment"
<!--<header android:fragment="uk.org.openseizuredetector.PrefActivity$BasicPrefsFragment"
android:icon="@drawable/icon_24x24"
android:title="@string/basic_settings_title"
android:summary="@string/basic_settings_summary" />
android:summary="@string/basic_settings_summary" />-->
<header android:fragment="uk.org.openseizuredetector.PrefActivity$GeneralPrefsFragment"
android:icon="@drawable/icon_24x24"
@@ -17,6 +17,11 @@
android:title="@string/alarms_settings_title"
android:summary="@string/alarms_settings_summary" />
<header android:fragment="uk.org.openseizuredetector.PrefActivity$LoggingPrefsFragment"
android:icon="@drawable/icon_24x24"
android:title="@string/logging_settings_title"
android:summary="@string/logging_settings_summary" />
<header android:fragment="uk.org.openseizuredetector.PrefActivity$SeizureDetectorPrefsFragment"
android:icon="@drawable/icon_24x24"
android:title="@string/seizure_detector_settings_title"

View File

@@ -1,129 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="LatchAlarms"
android:title="@string/latch_alarms_title"
android:summary="@string/latch_alarms_summary"
android:defaultValue="false" />
<CheckBoxPreference
android:key="AudibleAlarm"
android:title="@string/enable_audible_alarm_title"
android:summary="@string/enable_audible_alarm_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="AudibleWarning"
android:title="@string/enable_audible_warning_title"
android:summary="@string/enable_audible_warning_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="SMSAlarm"
android:title="@string/enable_sms_alarm_title"
android:summary="@string/enable_sms_alarm_summary"
android:defaultValue="false" />
<EditTextPreference
android:key="SMSNumbers"
android:title="@string/sms_numbers_title"
android:summary="@string/sms_numbers_summary"
android:defaultValue="" />
<EditTextPreference
android:key="SMSMsg"
android:title="@string/sms_message_title"
android:summary="@string/sms_message_summary"
android:defaultValue="**SEIZURE DETECTED**" />
<CheckBoxPreference
android:key="AudibleFaultWarning"
android:title="@string/enable_audible_fault_title"
android:summary="@string/enable_audible_fault_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="LogAlarms"
android:title="@string/log_alarms_title"
android:summary="@string/log_alarms_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="LogData"
android:title="@string/log_data_title"
android:summary="@string/log_data_summary"
android:defaultValue="false" />
<!-- <CheckBoxPreference
android:key="PreventSleep"
android:title="Prevent the screen from blanking."
android:summary="Prevent the screen from blanking while the application is running."
android:defaultValue="false" />
<EditTextPreference
android:key="UpdatePeriod"
android:title="Display Update Period (ms)."
android:summary="Display update period in miliseconds."
android:defaultValue="1000" />
-->
<EditTextPreference
android:key="AlarmFreqMin"
android:title="@string/alarm_freq_min_title"
android:summary="@string/alarm_freq_min_summary"
android:defaultValue="5" />
<EditTextPreference
android:key="AlarmFreqMax"
android:title="@string/alarm_freq_max_title"
android:summary="@string/alarm_freq_max_summary"
android:defaultValue="10" />
<EditTextPreference
android:key="WarnTime"
android:title="@string/warn_time_title"
android:summary="@string/warn_time_summary"
android:defaultValue="5" />
<EditTextPreference
android:key="AlarmTime"
android:title="@string/alarm_time_title"
android:summary="@string/alarm_time_summary"
android:defaultValue="10" />
<EditTextPreference
android:key="AlarmThresh"
android:title="@string/alarm_threshold_title"
android:summary="@string/alarm_threshold_summary"
android:defaultValue="100" />
<EditTextPreference
android:key="AlarmRatioThresh"
android:title="@string/alarm_ratio_thresh_title"
android:summary="@string/alarm_ratio_thresh_summary"
android:defaultValue="30" />
<CheckBoxPreference
android:key="FallActive"
android:title="@string/fall_detect_active_title"
android:summary=""
android:defaultValue="false" />
<EditTextPreference
android:key="FallThreshMin"
android:title="@string/fall_thresh_min_title"
android:summary=""
android:defaultValue="200" />
<EditTextPreference
android:key="FallThreshMax"
android:title="@string/fall_thresh_max_title"
android:summary=""
android:defaultValue="1200" />
<EditTextPreference
android:key="FallWindow"
android:title="@string/fall_window_title"
android:summary=""
android:defaultValue="1500" />
<EditTextPreference
android:key="AppRestartTimeout"
android:title="@string/app_restart_timeout_title"
android:summary="@string/app_restart_timeout_summary"
android:numeric="integer"
android:defaultValue="10" />
<EditTextPreference
android:key="FaultTimerPeriod"
android:title="@string/fault_timer_period_title"
android:summary="@string/fault_timer_period_summary"
android:numeric="integer"
android:defaultValue="30" />
</PreferenceScreen>

View File

@@ -19,7 +19,7 @@
android:summary="@string/AlarmThreshSummary"
android:title="@string/AlarmThreshTitle" />
<EditTextPreference
android:defaultValue="50"
android:defaultValue="57"
android:key="AlarmRatioThresh"
android:summary="@string/AlarmRatioThreshSummary"
android:title="@string/AlarmRatioThreshTitle" />
@@ -30,7 +30,7 @@
android:summary="@string/AlarmFreqMinSummary"
android:title="@string/AlarmFreqMinTitle" />
<EditTextPreference
android:defaultValue="10"
android:defaultValue="8"
android:key="AlarmFreqMax"
android:summary="@string/AlarmFreqMaxSummary"
android:title="@string/AlarmFreqMaxTitle" />
@@ -65,6 +65,24 @@
android:title="@string/HRThreshMaxTitle" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/O2SatSettingsTitle">
<CheckBoxPreference
android:defaultValue="false"
android:key="O2SatAlarmActive"
android:summary="@string/O2Sat_enabled_summary"
android:title="@string/O2Sat_enabled_title" />
<CheckBoxPreference
android:defaultValue="false"
android:key="O2SatNullAsAlarm"
android:summary="@string/O2SatNullAlarmSummary"
android:title="@string/O2SatNullAlarmTitle" />
<EditTextPreference
android:defaultValue="80"
android:key="O2SatThreshMin"
android:summary="@string/O2SatThreshMinSummary"
android:title="@string/O2SatThreshMinTitle" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/fall_detect_title">
<CheckBoxPreference
android:defaultValue="false"