Added notification to alert the user if data sharing is not enabled, and provide information on why it is good to enable it.

This commit is contained in:
Graham Jones
2022-02-27 19:43:01 +00:00
parent 26690efa11
commit dc27b5eb67
9 changed files with 297 additions and 96 deletions

View File

@@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="uk.org.openseizuredetector"
android:versionCode="92"
android:versionName="4.0.0k">
android:versionName="4.0.0l">
<!-- android:allowBackup="false" -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

View File

@@ -26,6 +26,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;
@@ -81,6 +83,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.
@@ -102,6 +105,7 @@ public class MainActivity extends AppCompatActivity {
mUtil.writeToSysLogFile("");
mUtil.writeToSysLogFile("* MainActivity Started *");
mUtil.writeToSysLogFile("MainActivity.onCreate()");
mContext = this;
// Initialise the User Interface
setContentView(R.layout.main);
@@ -139,8 +143,7 @@ public class MainActivity extends AppCompatActivity {
Log.v(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();
}
@@ -191,7 +194,42 @@ public class MainActivity extends AppCompatActivity {
}
});
// 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");
}
}
}
/**
@@ -292,7 +330,10 @@ public class MainActivity extends AppCompatActivity {
Log.i(TAG, "exception starting export activity " + ex.toString());
}
return true;
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");
@@ -370,7 +411,7 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"onStart()");
Log.i(TAG, "onStart()");
mUtil.writeToSysLogFile("MainActivity.onStart()");
SharedPreferences SP = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
@@ -404,7 +445,7 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"onStop() - unbinding from server");
Log.i(TAG, "onStop() - unbinding from server");
mUtil.writeToSysLogFile("MainActivity.onStop()");
mUtil.unbindFromServer(getApplicationContext(), mConnection);
mUiTimer.cancel();
@@ -828,8 +869,7 @@ public class MainActivity extends AppCompatActivity {
xVals.add(i + "-" + (i + 1) + " Hz");
if (mConnection.mSdServer != null) {
yBarVals.add(new BarEntry(mConnection.mSdServer.mSdData.simpleSpec[i], i));
}
else {
} else {
yBarVals.add(new BarEntry(i, i));
}
}
@@ -904,14 +944,14 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"onPause()");
Log.i(TAG, "onPause()");
mUtil.writeToSysLogFile("MainActivity.onPause()");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"onResume()");
Log.i(TAG, "onResume()");
mUtil.writeToSysLogFile("MainActivity.onResume()");
}
@@ -924,11 +964,40 @@ public class MainActivity extends AppCompatActivity {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.drawable.icon_24x24);
builder.setTitle("OpenSeizureDetector V" + versionName);
builder.setPositiveButton("OK", null);
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) {

View File

@@ -84,11 +84,15 @@ public class SdServer extends Service implements SdDataReceiver {
private String mUuidStr = "0f675b21-5a36-4fe7-9761-fd0c691651f3"; // UUID to Identify OSD.
// Notification ID
private int NOTIFICATION_ID = 1;
private int EVENT_NOTIFICATION_ID = 2;
private final int NOTIFICATION_ID = 1;
private final int EVENT_NOTIFICATION_ID = 2;
private final int DATASHARE_NOTIFICATION_ID = 3;
private String mNotChId = "OSD Notification Channel";
private CharSequence mNotChName = "OSD Notification Channel";
private String mNotChDesc = "OSD Notification Channel Description";
private String mEventNotChId = "OSD Event Notification Channel";
private CharSequence mEventNotChName = "OSD Event Notification Channel";
private String mEventNotChDesc = "OSD Event Notification Channel Description";
private NotificationManager mNM;
private NotificationCompat.Builder mNotificationBuilder;
@@ -100,7 +104,7 @@ public class SdServer extends Service implements SdDataReceiver {
private int mCancelAudiblePeriod = 10; // Cancel Audible Period in minutes
private long mCancelAudibleTimeRemaining = 0;
private FaultTimer mFaultTimer = null;
private CheckUnvalidatedEventsTimer mEventsTimer = null;
private CheckEventsTimer mEventsTimer = null;
private int mFaultTimerPeriod = 30; // Fault Timer Period in sec
private boolean mFaultTimerCompleted = false;
@@ -132,6 +136,7 @@ public class SdServer extends Service implements SdDataReceiver {
public boolean mLogDataRemote = false;
public boolean mLogDataRemoteMobile = false;
private String mAuthToken = null;
private long mEventsTimerPeriod = 60; // Number of seconds between checks to see if there are unvalidated remote events.
private long mEventDuration = 120; // event duration in seconds - uploads datapoints that cover this time range centred on the event time.
public long mDataRetentionPeriod = 1; // Prunes the local db so it only retains data younger than this duration (in days)
private long mRemoteLogPeriod = 60; // Period in seconds between uploads to the remote server.
@@ -324,7 +329,7 @@ public class SdServer extends Service implements SdDataReceiver {
}
if (mLogDataRemote) {
startValidatedEventsTimer();
startEventsTimer();
}
@@ -393,7 +398,7 @@ public class SdServer extends Service implements SdDataReceiver {
// Stop the Event timer
if (mEventsTimer != null) {
Log.d(TAG, "onDestroy(): Cancelling events timer");
stopValidatedEventsTimer();
stopEventsTimer();
}
// Stop the Cancel Alarm Latch timer
@@ -425,6 +430,7 @@ public class SdServer extends Service implements SdDataReceiver {
mUtil.writeToSysLogFile("SdServer.onDestroy - cancelling notification");
mNM.cancel(NOTIFICATION_ID);
mNM.cancel(EVENT_NOTIFICATION_ID);
mNM.cancel(DATASHARE_NOTIFICATION_ID);
// stop this service.
@@ -1513,18 +1519,18 @@ public class SdServer extends Service implements SdDataReceiver {
/**
* Start the events timer.
*/
public void startValidatedEventsTimer() {
public void startEventsTimer() {
if (mEventsTimer != null) {
Log.v(TAG, "startValidatedEventsTimer(): timer already running - not doing anything.");
mUtil.writeToSysLogFile("startValidatedEventsTimer() - timer already running");
Log.v(TAG, "startEventsTimer(): timer already running - not doing anything.");
mUtil.writeToSysLogFile("startEventsTimer() - timer already running");
} else {
Log.v(TAG, "startValidatedEventsTimer(): starting timer.");
mUtil.writeToSysLogFile("startValidatedEventsTimer() - starting timer");
Log.v(TAG, "startEventsTimer(): starting timer.");
mUtil.writeToSysLogFile("startEventsTimer() - starting timer");
runOnUiThread(new Runnable() {
public void run() {
mEventsTimer =
// Run every 10 sec (convert to ms.)
new CheckUnvalidatedEventsTimer(10 * 1000, 1000);
new CheckEventsTimer(mEventsTimerPeriod * 1000, 1000);
mEventsTimer.mIsRunning = true;
mEventsTimer.start();
}
@@ -1532,7 +1538,7 @@ public class SdServer extends Service implements SdDataReceiver {
}
}
public void stopValidatedEventsTimer() {
public void stopEventsTimer() {
if (mEventsTimer != null) {
Log.v(TAG, "stopEventsTimer(): timer already running - cancelling it.");
mUtil.writeToSysLogFile("stopEventsTimer() - stopping timer, setting mIsRunning to false");
@@ -1548,22 +1554,22 @@ public class SdServer extends Service implements SdDataReceiver {
* Periodically check if we have unvalidated events in the remote database.
* Show a notification if we do.
*/
private class CheckUnvalidatedEventsTimer extends CountDownTimer {
private class CheckEventsTimer extends CountDownTimer {
long mFirstUnvalidatedEvent;
public boolean mIsRunning = true;
public CheckUnvalidatedEventsTimer(long startTime, long interval) {
public CheckEventsTimer(long startTime, long interval) {
super(startTime, interval);
}
@Override
public void onFinish() {
Log.v(TAG, "CheckUnvalidatedEventsTimer.onFinish()");
Log.v(TAG, "CheckEventsTimer.onFinish()");
// Retrieve events from remote database
if (mLm.mWac.getEvents((JSONObject remoteEventsObj) -> {
Log.v(TAG, "CheckUnvalidatedEventsTimer.onFinish.getEvents.Callback()");
Log.v(TAG, "CheckEventsTimer.onFinish.getEvents.Callback()");
if (remoteEventsObj == null) {
Log.e(TAG, "CheckUnvalidatedEventsTimer.onFinish() Callback: Error Retrieving events");
Log.e(TAG, "CheckEventsTimer.onFinish() Callback: Error Retrieving events");
} else {
try {
JSONArray eventsArray = remoteEventsObj.getJSONArray("events");
@@ -1573,33 +1579,36 @@ public class SdServer extends Service implements SdDataReceiver {
JSONObject eventObj = eventsArray.getJSONObject(i);
Long id = eventObj.getLong("id");
String typeStr = eventObj.getString("type");
//Log.v(TAG,"CheckUnvalidatedEventsTimer: id="+id+", typeStr="+typeStr);
//Log.v(TAG,"CheckEventsTimer: id="+id+", typeStr="+typeStr);
if (typeStr.equals("null")) {
mFirstUnvalidatedEvent = id;
//Log.v(TAG,"CheckUnvalidatedEventsTimer:setting mFirstUnvalidatedEvent to "+mFirstUnvalidatedEvent);
//Log.v(TAG,"CheckEventsTimer:setting mFirstUnvalidatedEvent to "+mFirstUnvalidatedEvent);
}
}
Log.v(TAG, "CheckUnvalidatedEventsTimer.onFinish.callback - mFirstUnvalidatedEvent = " +
Log.v(TAG, "CheckEventsTimer.onFinish.callback - mFirstUnvalidatedEvent = " +
mFirstUnvalidatedEvent);
if (mFirstUnvalidatedEvent >= 0) {
showEventNotification(mFirstUnvalidatedEvent);
mNM.cancel(DATASHARE_NOTIFICATION_ID);
} else {
mNM.cancel(EVENT_NOTIFICATION_ID);
mNM.cancel(DATASHARE_NOTIFICATION_ID);
}
} catch (JSONException e) {
Log.e(TAG, "CheckUnvalidatedEventsTimer.onFinish(): Error Parsing remoteEventsObj: " + e.getMessage());
Log.e(TAG, "CheckEventsTimer.onFinish(): Error Parsing remoteEventsObj: " + e.getMessage());
//mUtil.showToast("Error Parsing remoteEventsObj - this should not happen!!!");
}
}
})) {
Log.v(TAG,"CheckUnvalidatedEventsTimer() - requested events");
Log.v(TAG, "CheckEventsTimer() - requested events");
} else {
Log.v(TAG,"CheckUnvalidatedEventsTimer() - Not Logged In");
Log.v(TAG, "CheckEventsTimer() - Not Logged In");
mNM.cancel(EVENT_NOTIFICATION_ID);
showDatashareNotification();
}
if (mIsRunning) {
// Restart this timer.
Log.v(TAG,"CheckUnvalidatedEventsTimer.onFinish() - mIsRunning is true, so re-starting timer");
Log.v(TAG, "CheckEventsTimer.onFinish() - mIsRunning is true, so re-starting timer");
start();
}
}
@@ -1616,17 +1625,31 @@ public class SdServer extends Service implements SdDataReceiver {
int iconId;
String titleStr;
Uri soundUri = null;
iconId = R.drawable.star_of_life_query_24x24;
// Initialise Notification channel for API level 26 and over
// from https://stackoverflow.com/questions/44443690/notificationcompat-with-api-26
NotificationManager nM = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getApplicationContext(), mEventNotChId);
if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel channel = new NotificationChannel(mEventNotChId,
mEventNotChName,
NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(mEventNotChDesc);
nM.createNotificationChannel(channel);
}
iconId = R.drawable.datasharing_query_24x24;
titleStr = getString(R.string.unvalidatedEventsTitle);
Intent i = new Intent(getApplicationContext(), LogManagerControlActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
i.setAction("None");
PendingIntent contentIntent =
PendingIntent.getActivity(getApplicationContext(),
0, i, PendingIntent.FLAG_UPDATE_CURRENT);
String contentStr = getString(R.string.please_confirm_seizure_events);
if (mNotificationBuilder != null) {
mNotification = mNotificationBuilder.setContentIntent(contentIntent)
Notification notification = notificationBuilder.setContentIntent(contentIntent)
.setSmallIcon(iconId)
.setColor(0x00ffffff)
.setAutoCancel(false)
@@ -1634,13 +1657,65 @@ public class SdServer extends Service implements SdDataReceiver {
.setContentText(contentStr)
.setOnlyAlertOnce(true)
.build();
mNM.notify(EVENT_NOTIFICATION_ID, mNotification);
} else {
Log.i(TAG, "showEventNotification() - notification builder is null, so not showing notification.");
nM.notify(EVENT_NOTIFICATION_ID, notification);
}
}
/**
* Show a notification asking the user to set-up data sharing.
*/
private void showDatashareNotification() {
Log.v(TAG, "showDatashareNotification()");
int iconId;
String titleStr;
Uri soundUri = null;
// Initialise Notification channel for API level 26 and over
// from https://stackoverflow.com/questions/44443690/notificationcompat-with-api-26
NotificationManager nM = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getApplicationContext(), mEventNotChId);
if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel channel = new NotificationChannel(mEventNotChId,
mEventNotChName,
NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(mEventNotChDesc);
nM.createNotificationChannel(channel);
}
iconId = R.drawable.datasharing_fault_24x24;
titleStr = getString(R.string.datasharing_notification_title);
Intent i = new Intent(getApplicationContext(), MainActivity.class);
i.putExtra("action", "showDataSharingDialog");
i.setAction("showDataSharingDialog");
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent =
PendingIntent.getActivity(getApplicationContext(),
0, i, PendingIntent.FLAG_UPDATE_CURRENT);
Intent loginIntent = new Intent(getApplicationContext(), AuthenticateActivity.class);
loginIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent loginPendingIntent =
PendingIntent.getActivity(getApplicationContext(),
0, loginIntent, PendingIntent.FLAG_UPDATE_CURRENT);
String contentStr = getString(R.string.datasharing_notification_text);
Notification notification = notificationBuilder
.setContentIntent(contentIntent)
.setSmallIcon(iconId)
.setColor(0x00ffffff)
.setAutoCancel(false)
.setContentTitle(titleStr)
.setContentText(contentStr)
.setOnlyAlertOnce(true)
.addAction(R.drawable.common_google_signin_btn_icon_dark, getString(R.string.login), loginPendingIntent)
.setPriority(0)
.build();
nM.notify(DATASHARE_NOTIFICATION_ID, notification);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

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"

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

@@ -35,6 +35,13 @@
android: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"
android:showAsAction="never|withText"
android:title="About Data Sharing..." />
</group>
<group android:id="@+id/grp4">
<item

View File

@@ -348,4 +348,20 @@
<string name="error_server_not_running">ERROR: OpenSeizureDetector Server is not running - please re-start it</string>
<string name="system_logs">System Logs</string>
<string name="logged_in_as_user_id">Logged in as User Id:</string>
<string name="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>
</resources>