V4.3.0a - added grouping function to data sharing event editing screen
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
OpenSeizureDetector Android App - Change Log
|
OpenSeizureDetector Android App - Change Log
|
||||||
============================================
|
============================================
|
||||||
|
V4.3.0a - 2024-10-01
|
||||||
|
- Added support for Android 14 (API 34) to allow publishing on Play Store.
|
||||||
|
- Improved the data sharing screen to show grouped events to reduce the number of events that need to be edited.
|
||||||
V4.2.12 - Fixed crash when pressing 'Install Watch App' button by hiding the button if the Pebble data source is not selected
|
V4.2.12 - Fixed crash when pressing 'Install Watch App' button by hiding the button if the Pebble data source is not selected
|
||||||
- Added a 'Help' and 'Troubleshooting' button and menu item to draw users' attention to the web site instructions.
|
- Added a 'Help' and 'Troubleshooting' button and menu item to draw users' attention to the web site instructions.
|
||||||
V4.2.11 - Updated permissions handling to support Android 14 (needed to publish on Play Store)
|
V4.2.11 - Updated permissions handling to support Android 14 (needed to publish on Play Store)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:versionCode="151"
|
android:versionCode="152"
|
||||||
android:versionName="4.2.12">
|
android:versionName="4.3.0a">
|
||||||
<!-- android:allowBackup="false" -->
|
<!-- android:allowBackup="false" -->
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public class EditEventActivity extends AppCompatActivity {
|
|||||||
private String mEventTypeStr = null;
|
private String mEventTypeStr = null;
|
||||||
private String mEventSubTypeStr = null;
|
private String mEventSubTypeStr = null;
|
||||||
private String mEventId;
|
private String mEventId;
|
||||||
|
private ArrayList<String> mEventIds; // For group editing
|
||||||
private String mEventNotes = "";
|
private String mEventNotes = "";
|
||||||
//private Date mEventDateTime;
|
//private Date mEventDateTime;
|
||||||
private RadioGroup mEventTypeRg;
|
private RadioGroup mEventTypeRg;
|
||||||
@@ -61,8 +62,15 @@ public class EditEventActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
Bundle extras = getIntent().getExtras();
|
Bundle extras = getIntent().getExtras();
|
||||||
if (extras != null) {
|
if (extras != null) {
|
||||||
String eventId = extras.getString("eventId");
|
mEventIds = extras.getStringArrayList("eventIds");
|
||||||
mEventId = eventId;
|
if (mEventIds != null && !mEventIds.isEmpty()) {
|
||||||
|
Log.v(TAG, "onCreate - Group Edit - eventIds=" + mEventIds.toString());
|
||||||
|
mEventId = mEventIds.get(0);
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "onCreate - Single Edit - eventId=" + extras.getString("eventId"));
|
||||||
|
mEventId = extras.getString("eventId");
|
||||||
|
mEventIds = null;
|
||||||
|
}
|
||||||
Log.v(TAG, "onCreate - mEventId=" + mEventId);
|
Log.v(TAG, "onCreate - mEventId=" + mEventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +305,7 @@ public class EditEventActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
Log.v(TAG, "onOK() - eventObj=" + mEventObj.toString());
|
Log.v(TAG, "onOK() - eventObj=" + mEventObj.toString());
|
||||||
|
|
||||||
|
// First we just save the open event, irrespective of whether it is a group edit or not.
|
||||||
try {
|
try {
|
||||||
mWac.updateEvent(mEventObj, new WebApiConnection.JSONObjectCallback() {
|
mWac.updateEvent(mEventObj, new WebApiConnection.JSONObjectCallback() {
|
||||||
@Override
|
@Override
|
||||||
@@ -320,9 +329,58 @@ public class EditEventActivity extends AppCompatActivity {
|
|||||||
mUtil.showToast("Error Updating Event");
|
mUtil.showToast("Error Updating Event");
|
||||||
updateUi();
|
updateUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a group edit, we need to update the other events in the group.
|
||||||
|
if (mEventIds != null && mEventIds.size() > 1) {
|
||||||
|
Log.v(TAG, "onOK() - Group Edit - updating other events in group");
|
||||||
|
updateGroupEventsSequentially(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private void updateGroupEventsSequentially(final int index) {
|
||||||
|
if (mEventIds == null || index >= mEventIds.size()) {
|
||||||
|
Log.v(TAG, "updateGroupEventsSequentially - All events updated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String eventId = mEventIds.get(index);
|
||||||
|
mWac.getEvent(eventId, new WebApiConnection.JSONObjectCallback() {
|
||||||
|
@Override
|
||||||
|
public void accept(JSONObject eventObj) {
|
||||||
|
if (eventObj == null) {
|
||||||
|
Log.e(TAG, "updateGroupEventsSequentially - ERROR: could not retrieve event " + eventId);
|
||||||
|
mUtil.showToast("Error Retrieving Event " + eventId);
|
||||||
|
updateGroupEventsSequentially(index + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
eventObj.put("id", eventId);
|
||||||
|
eventObj.put("type", mEventObj.getString("type"));
|
||||||
|
eventObj.put("subType", mEventObj.getString("subType"));
|
||||||
|
eventObj.put("desc", mEventObj.getString("desc"));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "updateGroupEventsSequentially - ERROR: " + e.getMessage());
|
||||||
|
updateGroupEventsSequentially(index + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mWac.updateEvent(eventObj, new WebApiConnection.JSONObjectCallback() {
|
||||||
|
@Override
|
||||||
|
public void accept(JSONObject updatedObj) {
|
||||||
|
if (updatedObj == null) {
|
||||||
|
Log.e(TAG, "updateGroupEventsSequentially - ERROR: update failed for " + eventId);
|
||||||
|
mUtil.showToast("Error Updating Event " + eventId);
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "updateGroupEventsSequentially - Updated event " + eventId + " OK");
|
||||||
|
mUtil.showToast("Event " + eventId + " Updated OK");
|
||||||
|
}
|
||||||
|
updateGroupEventsSequentially(index + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
RadioGroup.OnCheckedChangeListener onEventTypeChange =
|
RadioGroup.OnCheckedChangeListener onEventTypeChange =
|
||||||
new RadioGroup.OnCheckedChangeListener() {
|
new RadioGroup.OnCheckedChangeListener() {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -42,13 +43,17 @@ import java.lang.reflect.Field;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class LogManagerControlActivity extends AppCompatActivity {
|
public class LogManagerControlActivity extends AppCompatActivity {
|
||||||
private String TAG = "LogManagerControlActivity";
|
private String TAG = "LogManagerControlActivity";
|
||||||
|
private static final long GROUPING_WINDOW_MINUTES = 3;
|
||||||
|
private static final long GROUPING_WINDOW_MS = GROUPING_WINDOW_MINUTES * 60 * 1000;
|
||||||
private LogManager mLm;
|
private LogManager mLm;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private UiTimer mUiTimer;
|
private UiTimer mUiTimer;
|
||||||
@@ -63,6 +68,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
private Integer mUiTimerPeriodSlow = 60000; // 60 seconds - once data has been received and UI populated we only update once per minute.
|
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 boolean mUpdateSysLog = true;
|
||||||
private Menu mMenu;
|
private Menu mMenu;
|
||||||
|
private CheckBox mGroupEventsCb; // Declare the CheckBox member
|
||||||
//private Integer UI_MODE_LOCAL = 0;
|
//private Integer UI_MODE_LOCAL = 0;
|
||||||
//private Integer UI_MODE_SHARED = 1;
|
//private Integer UI_MODE_SHARED = 1;
|
||||||
//private Integer mUiMode = UI_MODE_SHARED;
|
//private Integer mUiMode = UI_MODE_SHARED;
|
||||||
@@ -118,6 +124,21 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
(CheckBox) findViewById(R.id.include_nda_cb);
|
(CheckBox) findViewById(R.id.include_nda_cb);
|
||||||
includeNDACb.setOnCheckedChangeListener(onIncludeNDACb);
|
includeNDACb.setOnCheckedChangeListener(onIncludeNDACb);
|
||||||
|
|
||||||
|
mGroupEventsCb = findViewById(R.id.group_events_cb);
|
||||||
|
mGroupEventsCb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
|
// When the checkbox state changes, re-process and update the UI
|
||||||
|
if (mRemoteEventsList != null && !mRemoteEventsList.isEmpty()) {
|
||||||
|
if (isChecked) {
|
||||||
|
createGroupedEventsList();
|
||||||
|
}
|
||||||
|
updateUi(); // Update UI to reflect grouped or non-grouped list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
ListView lv = (ListView) findViewById(R.id.eventLogListView);
|
ListView lv = (ListView) findViewById(R.id.eventLogListView);
|
||||||
lv.setOnItemClickListener(onEventListClick);
|
lv.setOnItemClickListener(onEventListClick);
|
||||||
|
|
||||||
@@ -219,7 +240,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
updateUi();
|
updateUi();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG,"ERROR: initialiseServiceConnection() - mLm is null");
|
Log.e(TAG, "ERROR: initialiseServiceConnection() - mLm is null");
|
||||||
mUtil.showToast(getString(R.string.error_failed_to_start_log_manager));
|
mUtil.showToast(getString(R.string.error_failed_to_start_log_manager));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,6 +248,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private void getRemoteEvents(boolean includeWarnings, boolean includeNDA) {
|
private void getRemoteEvents(boolean includeWarnings, boolean includeNDA) {
|
||||||
mRemoteEventsList = null; // clear existing data
|
mRemoteEventsList = null; // clear existing data
|
||||||
|
mGroupedRemoteEventsList = null;
|
||||||
// Retrieve events from remote database
|
// Retrieve events from remote database
|
||||||
mLm.mWac.getEvents((JSONObject remoteEventsObj) -> {
|
mLm.mWac.getEvents((JSONObject remoteEventsObj) -> {
|
||||||
Log.v(TAG, "getRemoteEvents()");
|
Log.v(TAG, "getRemoteEvents()");
|
||||||
@@ -253,7 +275,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
String dataTime = "null";
|
String dataTime = "null";
|
||||||
if (!eventObj.isNull("dataTime")) {
|
if (!eventObj.isNull("dataTime")) {
|
||||||
dataTime = eventObj.getString("dataTime");
|
dataTime = eventObj.getString("dataTime");
|
||||||
Log.v(TAG, "getRemoteEvents() - dataTime=" + dataTime);
|
//Log.v(TAG, "getRemoteEvents() - dataTime=" + dataTime);
|
||||||
}
|
}
|
||||||
String typeStr = "null";
|
String typeStr = "null";
|
||||||
if (!eventObj.isNull("type")) {
|
if (!eventObj.isNull("type")) {
|
||||||
@@ -282,27 +304,141 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
Log.v(TAG, "getRemoteEvents - skipping warning or NDA record");
|
Log.v(TAG, "getRemoteEvents - skipping warning or NDA record");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createGroupedEventsList();
|
|
||||||
Log.v(TAG, "getRemoteEvents() - set mRemoteEventsList(). Updating UI");
|
// Sort the remote events list by date, descending (newest first)
|
||||||
|
Log.v(TAG, "getRemoteEvents() - Sorting mRemoteEventsList by date");
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault()); // Adjust format if needed
|
||||||
|
Collections.sort(mRemoteEventsList, (event1, event2) -> {
|
||||||
|
try {
|
||||||
|
String dt1Str = event1.get("dataTime");
|
||||||
|
String dt2Str = event2.get("dataTime");
|
||||||
|
if (
|
||||||
|
dt1Str == null
|
||||||
|
|| dt2Str == null
|
||||||
|
|| dt1Str.equals("null")
|
||||||
|
|| dt2Str.equals("null"))
|
||||||
|
return 0;
|
||||||
|
Date date1 = sdf.parse(dt1Str);
|
||||||
|
Date date2 = sdf.parse(dt2Str);
|
||||||
|
return date2.compareTo(date1); // Descending
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.e(TAG, "Error parsing date for sorting: " + e.getMessage());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (mGroupEventsCb.isChecked()) { // Check if grouping is enabled
|
||||||
|
createGroupedEventsList();
|
||||||
|
Log.v(TAG, "getRemoteEvents() - created grouped events. Updating UI");
|
||||||
|
} else {
|
||||||
|
mGroupedRemoteEventsList = null; // Ensure grouped list is cleared if not used
|
||||||
|
Log.v(TAG, "getRemoteEvents() - grouping disabled. Updating UI with flat list.");
|
||||||
|
}
|
||||||
updateUi();
|
updateUi();
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(TAG, "getRemoteEvents(): Error Parsing remoteEventsObj: " + e.getMessage());
|
Log.e(TAG, "getRemoteEvents(): Error Parsing remoteEventsObj: " + e.getMessage());
|
||||||
mUtil.showToast("Error Parsing remoteEventsObj - this should not happen!!!");
|
mUtil.showToast("Error Parsing remoteEventsObj - this should not happen!!!");
|
||||||
mRemoteEventsList = null;
|
mRemoteEventsList = null;
|
||||||
|
mGroupedRemoteEventsList = null;
|
||||||
|
updateUi(); // Update UI to show error state
|
||||||
}
|
}
|
||||||
//Log.v(TAG, "getRemoteEvents(): mRemoteEventsList = " + mRemoteEventsList.toString());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* createGroupedEventsList()
|
||||||
|
* Reads the complete list of remote events mRemoteEventsList and creates a new list mGroupedRemoteEventsList
|
||||||
|
* where each item is a list of events that comprise a group based on time (all events within a 3 minute period are grouped together).
|
||||||
|
*/
|
||||||
private void createGroupedEventsList() {
|
private void createGroupedEventsList() {
|
||||||
/**
|
|
||||||
* Reads the complete list of remote events mRemoteEventsList and creates a new list mGroupedRemoteEventsList
|
|
||||||
* where each item is a list of events that comprise a group based on time (all events within a 3 minute period are grouped together).
|
|
||||||
*/
|
|
||||||
Log.i(TAG, "createGroupedEventsList()");
|
Log.i(TAG, "createGroupedEventsList()");
|
||||||
mGroupedRemoteEventsList = new ArrayList<ArrayList<HashMap<String,String>>>();
|
/**
|
||||||
// FIXME - Make this do something!
|
* createGroupedEventsList()
|
||||||
|
* Reads the complete list of remote events mRemoteEventsList (sorted newest first)
|
||||||
|
* and creates a new list mGroupedRemoteEventsList
|
||||||
|
* where each item is a list of events that comprise a group based on time.
|
||||||
|
*/
|
||||||
|
mGroupedRemoteEventsList = new ArrayList<>();
|
||||||
|
if (mRemoteEventsList == null || mRemoteEventsList.isEmpty()) {
|
||||||
|
Log.i(TAG, "createGroupedEventsList() - mRemoteEventsList is null or empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to parse date strings to long timestamps.
|
||||||
|
// Adjust the SimpleDateFormat pattern to match your "dataTime" format.
|
||||||
|
// If "dataTime" is already a timestamp (long), you can use it directly.
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault()); // Example format
|
||||||
|
|
||||||
|
ArrayList<HashMap<String, String>> currentGroup = null;
|
||||||
|
long lastEventTimeInGroup = 0;
|
||||||
|
|
||||||
|
for (HashMap<String, String> event : mRemoteEventsList) {
|
||||||
|
String dataTimeString = event.get("dataTime");
|
||||||
|
if (dataTimeString == null || dataTimeString.equals("null")) {
|
||||||
|
Log.w(TAG, "Event has null or invalid dataTime: " + event.get("id"));
|
||||||
|
continue; // Skip events with no valid time
|
||||||
|
}
|
||||||
|
|
||||||
|
long currentEventTime;
|
||||||
|
try {
|
||||||
|
Date eventDate = sdf.parse(dataTimeString);
|
||||||
|
if (eventDate == null) {
|
||||||
|
Log.w(TAG, "Could not parse dataTime: " + dataTimeString + " for event: " + event.get("id"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
currentEventTime = eventDate.getTime();
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.e(TAG, "Error parsing date string: " + dataTimeString + " - " + e.getMessage());
|
||||||
|
continue; // Skip if date can't be parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentGroup == null || (lastEventTimeInGroup - currentEventTime) > GROUPING_WINDOW_MS) {
|
||||||
|
// Start a new group
|
||||||
|
if (currentGroup != null) {
|
||||||
|
moveFirstAlarmToFront(currentGroup); // Move the first ALARM event to the front of the group)
|
||||||
|
mGroupedRemoteEventsList.add(currentGroup);
|
||||||
|
}
|
||||||
|
currentGroup = new ArrayList<>();
|
||||||
|
currentGroup.add(event);
|
||||||
|
lastEventTimeInGroup = currentEventTime;
|
||||||
|
} else {
|
||||||
|
// Add to the current group
|
||||||
|
currentGroup.add(event);
|
||||||
|
// lastEventTimeInGroup remains the time of the first event added to this group (newest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the last group if it exists
|
||||||
|
if (currentGroup != null && !currentGroup.isEmpty()) {
|
||||||
|
moveFirstAlarmToFront(currentGroup); // Move the first ALARM event to the front of the group
|
||||||
|
mGroupedRemoteEventsList.add(currentGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "createGroupedEventsList() - Grouped " + mRemoteEventsList.size() +
|
||||||
|
" events into " + mGroupedRemoteEventsList.size() + " groups.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* moveFirstAlarmToFront() - This method checks the group for the first
|
||||||
|
* event with an ALARM state (osdAlarmState = 2) and makes that event the
|
||||||
|
* first in the list.
|
||||||
|
*
|
||||||
|
* @param group An ArrayList of HashMaps representing a group of events.
|
||||||
|
*/
|
||||||
|
private void moveFirstAlarmToFront(ArrayList<HashMap<String, String>> group) {
|
||||||
|
//Log.i(TAG, "moveFirstAlarmToFront() - Checking group of size: " + group.size());
|
||||||
|
for (int i = 0; i < group.size(); i++) {
|
||||||
|
HashMap<String, String> event = group.get(i);
|
||||||
|
String alarmStateStr = event.get("osdAlarmState");
|
||||||
|
if (alarmStateStr != null && alarmStateStr.equals("2")) { // ALARM is 2
|
||||||
|
//Log.v(TAG," moveFirstAlarmToFront() - Found ALARM event at index: " + i);
|
||||||
|
if (i != 0) {
|
||||||
|
group.remove(i);
|
||||||
|
group.add(0, event);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUi() {
|
private void updateUi() {
|
||||||
@@ -353,13 +489,27 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
pb.setIndeterminate(false);
|
pb.setIndeterminate(false);
|
||||||
pb.setVisibility(View.INVISIBLE);
|
pb.setVisibility(View.INVISIBLE);
|
||||||
ListView lv = (ListView) findViewById(R.id.remoteEventsLv);
|
ListView lv = (ListView) findViewById(R.id.remoteEventsLv);
|
||||||
ListAdapter adapter = new RemoteEventsAdapter(LogManagerControlActivity.this, mRemoteEventsList, R.layout.log_entry_layout_remote,
|
|
||||||
new String[]{"id", "dataTime", "type", "subType", "osdAlarmStateStr", "desc"},
|
if (mGroupEventsCb.isChecked() && mGroupedRemoteEventsList != null) {
|
||||||
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,
|
// Show only the first event of each group
|
||||||
R.id.event_alarmState_remote_tv, R.id.event_notes_remote_tv});
|
ArrayList<HashMap<String, String>> displayList = new ArrayList<>();
|
||||||
lv.setAdapter(adapter);
|
for (ArrayList<HashMap<String, String>> group : mGroupedRemoteEventsList) {
|
||||||
//Log.i(TAG,"adapter[0]="+adapter.getItem(0));
|
displayList.add(group.get(0));
|
||||||
//Log.i(TAG,"adapter[3]="+adapter.getItem(3));
|
}
|
||||||
|
ListAdapter adapter = new RemoteEventsAdapter(LogManagerControlActivity.this, displayList, 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);
|
||||||
|
} else if (mRemoteEventsList != null) {
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "UpdateUi: No Remote Events");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//mUtil.showToast("No Remote Events");
|
//mUtil.showToast("No Remote Events");
|
||||||
Log.i(TAG, "UpdateUi: No Remote Events");
|
Log.i(TAG, "UpdateUi: No Remote Events");
|
||||||
@@ -656,15 +806,26 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
new AdapterView.OnItemClickListener() {
|
new AdapterView.OnItemClickListener() {
|
||||||
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
|
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
|
||||||
Log.v(TAG, "onItemClicKListener() - Position=" + position + ", id=" + id);// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
|
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");
|
if (mGroupEventsCb.isChecked() && mGroupedRemoteEventsList != null) {
|
||||||
Log.d(TAG, "onItemClickListener(): eventId=" + eventId + ", eventObj=" + eventObj);
|
Log.v(TAG,"onItemClickListener() - Creating Grouped Events List from Position=" + position);
|
||||||
if (eventId != null) {
|
// Get the group for this position
|
||||||
|
ArrayList<HashMap<String, String>> group = mGroupedRemoteEventsList.get(position);
|
||||||
|
ArrayList<String> eventIds = new ArrayList<>();
|
||||||
|
for (HashMap<String, String> event : group) {
|
||||||
|
Log.v(TAG,"onItemClickListener() - Adding event to edit list: " + event.get("id"));
|
||||||
|
eventIds.add(event.get("id"));
|
||||||
|
}
|
||||||
|
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
|
||||||
|
i.putStringArrayListExtra("eventIds", eventIds);
|
||||||
|
startActivity(i);
|
||||||
|
} else {
|
||||||
|
Log.v(TAG,"onItemClickListener() - Editing Single event at Position=" + position);
|
||||||
|
HashMap<String, String> eventObj = (HashMap<String, String>) adapter.getItemAtPosition(position);
|
||||||
|
String eventId = eventObj.get("id");
|
||||||
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
|
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
|
||||||
i.putExtra("eventId", eventId);
|
i.putExtra("eventId", eventId);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
} else {
|
|
||||||
mUtil.showToast("You Must Wait for Event to Upload before Editing it");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -673,13 +834,31 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
new AdapterView.OnItemClickListener() {
|
new AdapterView.OnItemClickListener() {
|
||||||
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
|
public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
|
||||||
Log.v(TAG, "onRemoteEventList Click() - Position=" + position + ", id=" + id);// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
|
Log.v(TAG, "onRemoteEventList Click() - Position=" + position + ", id=" + id);// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
|
||||||
HashMap<String, String> eventObj = (HashMap<String, String>) adapter.getItemAtPosition(position);
|
Log.v(TAG, "onItemClickListener() - Position=" + position + ", id=" + id);// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
|
||||||
String eventId = eventObj.get("id");
|
|
||||||
Log.d(TAG, "onItemClickListener(): eventId=" + eventId + ", eventObj=" + eventObj);
|
if (mGroupEventsCb.isChecked() && mGroupedRemoteEventsList != null) {
|
||||||
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
|
Log.v(TAG,"onItemClickListener() - Creating Grouped Events List from Position=" + position);
|
||||||
i.putExtra("eventId", eventId);
|
// Get the group for this position
|
||||||
startActivity(i);
|
ArrayList<HashMap<String, String>> group = mGroupedRemoteEventsList.get(position);
|
||||||
|
ArrayList<String> eventIds = new ArrayList<>();
|
||||||
|
for (HashMap<String, String> event : group) {
|
||||||
|
Log.v(TAG,"onItemClickListener() - Adding event to edit list: " + event.get("id"));
|
||||||
|
eventIds.add(event.get("id"));
|
||||||
|
}
|
||||||
|
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
|
||||||
|
i.putStringArrayListExtra("eventIds", eventIds);
|
||||||
|
startActivity(i);
|
||||||
|
} else {
|
||||||
|
Log.v(TAG,"onItemClickListener() - Editing Single event at Position=" + position);
|
||||||
|
HashMap<String, String> eventObj = (HashMap<String, String>) adapter.getItemAtPosition(position);
|
||||||
|
String eventId = eventObj.get("id");
|
||||||
|
Intent i = new Intent(getApplicationContext(), EditEventActivity.class);
|
||||||
|
i.putExtra("eventId", eventId);
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -787,6 +966,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void showDataSharingDialog() {
|
private void showDataSharingDialog() {
|
||||||
mUtil.writeToSysLogFile("MainActivity.showDataSharingDialog()");
|
mUtil.writeToSysLogFile("MainActivity.showDataSharingDialog()");
|
||||||
View aboutView = getLayoutInflater().inflate(R.layout.data_sharing_dialog_layout, null, false);
|
View aboutView = getLayoutInflater().inflate(R.layout.data_sharing_dialog_layout, null, false);
|
||||||
|
|||||||
@@ -369,13 +369,13 @@ public class WebApiConnection_osdapi extends WebApiConnection {
|
|||||||
new Response.Listener<String>() {
|
new Response.Listener<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(String response) {
|
public void onResponse(String response) {
|
||||||
Log.v(TAG, "Response is: " + response);
|
Log.v(TAG, "updateEvent.onResponse(): Response is: " + response);
|
||||||
mServerConnectionOk = true;
|
mServerConnectionOk = true;
|
||||||
try {
|
try {
|
||||||
JSONObject retObj = new JSONObject(response);
|
JSONObject retObj = new JSONObject(response);
|
||||||
callback.accept(retObj);
|
callback.accept(retObj);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(TAG, "getEventTypes.onRespons(): Error: " + e.getMessage() + "," + e.toString());
|
Log.e(TAG, "updateEvent.onResponse(): Error: " + e.getMessage() + "," + e.toString());
|
||||||
callback.accept(null);
|
callback.accept(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,9 +385,9 @@ public class WebApiConnection_osdapi extends WebApiConnection {
|
|||||||
public void onErrorResponse(VolleyError error) {
|
public void onErrorResponse(VolleyError error) {
|
||||||
mServerConnectionOk = false;
|
mServerConnectionOk = false;
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
Log.e(TAG, "Create Event Error: " + error.toString() + ", message:" + error.getMessage());
|
Log.e(TAG, "updateEvent.onErrorResponse(): Error: " + error.toString() + ", message:" + error.getMessage());
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Create Event Error - returned null response");
|
Log.e(TAG, "updateEvent.onErrorResponse(): Error - returned null response");
|
||||||
}
|
}
|
||||||
callback.accept(null);
|
callback.accept(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,48 +121,67 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/authStatusTv"
|
android:id="@+id/authStatusTv"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/not_authenticated" />
|
android:text="@string/not_authenticated"
|
||||||
|
android:layout_marginEnd="8dp"/> <!-- Space after TextView -->
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/auth_button"
|
android:id="@+id/auth_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/authenticate" />
|
android:text="@string/authenticate"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp" /> <!-- Space after this button -->
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/refresh_button"
|
android:id="@+id/refresh_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/refreshBtn" />
|
android:text="@string/refreshBtn"
|
||||||
|
android:layout_marginStart="8dp" /> <!-- Space before this button -->
|
||||||
|
<!-- No marginEnd needed if ProgressBar is last and you want it close,
|
||||||
|
or add marginEnd if you want space before ProgressBar too -->
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/remoteAccessPb"
|
android:id="@+id/remoteAccessPb"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="8dp"/> <!-- Space before ProgressBar -->
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/group_events_cb"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/group_remote_events"
|
||||||
|
android:checked="true" />
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/include_warnings_cb"
|
android:id="@+id/include_warnings_cb"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/include_warnings"></CheckBox>
|
android:layout_weight="1"
|
||||||
|
android:text="@string/include_warnings"/>
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/include_nda_cb"
|
android:id="@+id/include_nda_cb"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/include_nda"></CheckBox>
|
android:layout_weight="1"
|
||||||
|
android:text="@string/include_nda"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
|
|||||||
42
app/src/main/res/layout/grouped_event_entry_layout.xml
Normal file
42
app/src/main/res/layout/grouped_event_entry_layout.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<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"
|
||||||
|
android:id="@+id/group_event_time_tv" />
|
||||||
|
<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/group_event_alarmState_tv"
|
||||||
|
/>
|
||||||
|
<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="uploaded"
|
||||||
|
android:id="@+id/group_event_uploaded"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
<!-- <TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="dataJSON"
|
||||||
|
android:id="@+id/event_dataJSON" />
|
||||||
|
-->
|
||||||
|
</LinearLayout>
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
<string name="app_name">OpenSeizureDetector</string>
|
<string name="app_name">OpenSeizureDetector</string>
|
||||||
<string name="changelog">
|
<string name="changelog">
|
||||||
"\n
|
"\n
|
||||||
|
\nV4.3.0a - Added support for Android 14 (API 34) and above.
|
||||||
|
\n - Simplified data sharing editor by grouping events for editing.
|
||||||
\nV4.2.12 - Added butons and menu items for 'Help' and 'Troubleshooting' to point users to the web page instructoins.
|
\nV4.2.12 - Added butons and menu items for 'Help' and 'Troubleshooting' to point users to the web page instructoins.
|
||||||
\nV4.2 - Added support for PineTime and BangleJS Watches using Bluetooth data source.
|
\nV4.2 - Added support for PineTime and BangleJS Watches using Bluetooth data source.
|
||||||
\n - Added support for Version 2 of the Garmin watch app, which reduces battery drain
|
\n - Added support for Version 2 of the Garmin watch app, which reduces battery drain
|
||||||
@@ -588,4 +590,5 @@
|
|||||||
<string name="DefaultSMSFalseAlarmMsgText">False Alarm, Sorry!</string>
|
<string name="DefaultSMSFalseAlarmMsgText">False Alarm, Sorry!</string>
|
||||||
<string name="sms_false_alarm_message_summary">Text of \'False Alarm\' SMS message</string>
|
<string name="sms_false_alarm_message_summary">Text of \'False Alarm\' SMS message</string>
|
||||||
<string name="sms_false_alarm_message_title">SMS False Alarm Message</string>
|
<string name="sms_false_alarm_message_title">SMS False Alarm Message</string>
|
||||||
|
<string name="group_remote_events">Group Remote Events</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
releases/app-release-4.3.0a.apk
Normal file
BIN
releases/app-release-4.3.0a.apk
Normal file
Binary file not shown.
Reference in New Issue
Block a user