R9555/0e0aa05567d5master
/
README.md
Android app for multimodal data acquisition
This repository contains an app for acquiring and storing data from multiple sensors. Currently, can be used with the following devices:
- Empatica E4
- Tablet/Smartphone built-in sensors
- MetaMotion R
Usage
In order to improve reliability, a bipartite structure has been implemented. In particular, the Main Activity acts as an interface between the user and the main service that constitutes the principal actor. The latter performs scans, handles the user's requests to connect remote devices, all the unexpected disconnection that may happen and receives the data from the wireless sensors.
See App.pdf for the complete block diagram of the App including three accessory activities (in light blue).
Class DeviceManager
DeviceManager is designed to abstract the devices’ individual APIs by providing standardized methods for the interaction with the devices themselves.
Each device usually comes with its own API providing methods for authentication, connection, receiving the device’s data and parsing it. To be able to standardize the main operations that have to be performed on different devices, an interface called InterfaceManager has been defined. For every device, the methods of the said interface will be implemented according to the device's API. In this way, we will have as many objects implementing the InterfaceManager as the devices we are going to include in our setup. The interface contains the following method signatures:
java public interface InterfaceManager { void scan(); void isDevice(String name, String address); void stopScan(); void connect(String id); void disconnect(String id); void readBatteryLevel(String id); }
In particular, scan() is called whenever the user requires a scan. It has to be implemented according to the wireless technology the device uses. isDevice() is called every time the previous scan returns a result and it has to be implemented to detect if the device that has been scanned is the one we are interested in. Indeed, it can happen that the scan returns several devices the user is not concerned about. stopScan() is called to interrupt a scan. connect() is called whenever it is required to connect a device that has been scanned and has to be implemented with the method provided by the device-specific API to connect. disconnect() has to be implemented with the method provided by the device-specific API to disconnect. Finally, readBatteryLevel() is called in order to update the battery level meter on the GUI and has to be implemented according to the device-specific API method to get the battery level.
Thus, for every device we have a deviceManager object that implements the previous interface. The advantage is that, to perform the same operation on different devices (for example, to connect or to require the battery level), we have just to call the corresponding method of the deviceManager corresponging to the device we are considering.
Class Device
The architecture of the high-level description of every device consists of three levels; the first of them concerns the device itself. The abstract class Device represents this first level. It includes the methods for setting the name of the device and including all the sensors the device embeds in. The second level concerns each embedded sensor, represented by the abstract class Sensor (the accelerometer, the PPG sensor, the EDA sensor and the SKT sensor, in case of the Empatica E4). To add a sensor to a device, the setSensors() method has to be implemented and a Sensor object has to be added to the sensorArrayList list (field of Device). The method setSensors() is called by the constructor of the class Device. The third level represents the measurement the sensor provides, represented by the abstract class Data. For the Empatica E4, the accelerometer data is made up of three Data objects: acceleration in the three orthogonal axis X, Y, and Z. To add a Data to a specific sensor, the method addData() has to be called in the constructor with a Data object as a parameter.
Service Manager
The Service ServiceManager constitutes the main actor of the entire application (see Figure App.pdf for the complete block diagram of the App). In particular, in its onCreate() method, it supplies a persistent notification to avoid the system killing it when reclaiming more memory (such as to display a large page in a web browser) and it instantiates the device-specific DeviceMangers for the devices the user has chosen. To do so, the service has a local list of the devices that have been chosen by the user. Let chosenDevices be this list. At this point, whatever application component binds to it will be able to:
- replace chosenDevices with the user’s new preferences;
- launch a Bluethoot scan;
- stop the Bluethoot scan;
- connect a specific device;
- disconnect a specific device;
- read the battery level of a specific device;
- specify if storing or rejecting the data of a specific device;
- change the text of the persistent notification or display a new temporary notification.
At the same time, the client will be notified about:
- the list of the scanned devices (if among the required ones specified in chosenDevices);
- the list of the connected devices;
- the list of the devices whose data is currently stored in the phone memory;
- unexpected disconnections (if the device cannot provide automatic reconnection and requires the user interaction).
MainActivity
As stated, the main role of the Main Activity is to provide the user with information related to the available devices and serve as an interface to the ServiceManager that takes care of all the important tasks such as launching Bluetooth scans, receiving data and data storage.
In the onCreate(), we recollect the preferences expressed from the user in the SettingsActivity regarding which device have to be taken into account and starts the ServiceManager service if not already running. The activity then generates a local list of the devices (instances of the class Device) chosen by the user. In the onResume(), the Main Activity binds to ServiceManager and communicate to it the list of the chosen devices collected from the user preferences for details about service binding). Then, the local list of devices is updated with the information from ServiceManager. In particular, for every device in the list, the activity asks the service if:
- The device has been detected;
- The device is connected (only if the previous inquiry returned true);
- The battery level (only if the previous inquiry returned true);
- The device’s data is actually stored in the smartphone’s memory.
The Floating Action Button with the pill icon is used to pop out an alertDialog containing a check box list containing several drugs. The user can select one or more. After dissmissing the alertDialog, the data is saved in the directory Documents/LogFromUser of the tablet/smartphone (Documents is a standard directory Android has in which to place documents that have been created by the user).
The two switches allow the user to log their prodromal phase and pain. After toggling off the pain switch, a questionnaire is displayed. The json containing the answers of the user is then saved in Documents/Questionnaires.
SettingsActivity
The Settings Activity contains a list of checkboxes the user can use to express his preference in terms of devices to use. It also includes a switch to enable or disable the automatic data storage and a textbox to insert the API key needed for some devices. To access it, the user is asked to enter a password. This password is ciao. (Only meant to avoid the final user to mess up with the settings, nothing secret here).
Monitor Activity
The Monitor Activity allows the user to visualize a plot in real-time of the data streamed by the connected devices. It is mainly used during the set-up to verify if the devices are correctly worn.