The ESM sensor adds support for user-provided context by leveraging mobile Experience Sampling Method (ESM). The ESM questionnaires can be triggered by context, time or on-demand, locally or remotely (within your study on AWARE’s dashboard). Although user-subjective, this sensor allows crowdsourcing information that is challenging to instrument with sensors.

Public functions

  • ESM.queueESM( Context context, String esmJSON ): Adds ESMs (the string representation of JSON) to the queue.

Settings

  • Aware_Preferences.STATUS_ESM: true or false to activate or deactivate ESM sensor.

AWARE provides a flexible ESM questionnaire building mechanism. Diverse ESM questions can be chained together to support a step-by-step questionnaire, and conditions can be used to create alternate questionnaire flows. The following elements are common to all ESMs:

  • esm_type: a number identifying the kind of ESM
  • esm_title: the title of the ESM (usually short, between 30-40 characters)
  • esm_instructions: instructions to answer the ESM
  • esm_submit: the label for the save button. If creating a queue, this will move to the next queued question
  • esm_expiration_threshold: how long the user has to answer the question after it is displayed in the screen. The timer stops when the user touches the dialog. The ESM does not expire if set to 0 (zero) and will show a notification in the notification bar. If set to anything > 0, the ESM instantly prompts the user to answer the questionnaire, and will expire if the user has not touched the dialog.
  • esm_notification_timeout: time after which the ESM notification is dismissed and the whole ESM queue expires (in case expiration threshold is set to 0).
  • esm_trigger: a freeform string you can define for what could have triggered the ESM, useful for traceability and data analysis.
NOTE: Answering an ESM is never mandatory. If the user presses the Home or the Back button, the ESM is dismissed. We track the ESM statuses: new, dismissed, expired, branched or answered. An ESM does not have a default answer, i.e., if no answer as given, it will be blank.

aware-broadcasts

  • ESM.ACTION_AWARE_ESM_ANSWERED: broadcasted when the user answered an ESM question.
  • ESM.ACTION_AWARE_ESM_DISMISSED: broadcasted when the user dismissed an ESM question.
  • ESM.ACTION_AWARE_ESM_EXPIRED: broadcasted when the ESM expires.
  • ESM.ACTION_AWARE_ESM_QUEUE_COMPLETE: broadcasted when there are no more ESM questions on the ESM queue.
  • ESM.ACTION_AWARE_ESM_QUEUE_STARTED: broadcasted when the first ESM question from the queue of ESMs is presented to the user.
  • ESM.ACTION_AWARE_QUEUE_ESM: received broadcast to add a new ESM to the ESM queue. One extra is required when issuing this broadcast:
    • ESM.EXTRA_ESM: a string with a JSON array with the ESM definition, see previous section.

aware-providers

ESM Data

Contains the raw sensor data.

ESM_Data.CONTENT_URI
content://com.aware.provider.esm/esms

Table field Field type Description
_id INTEGER primary key, auto incremented
timestamp REAL Unixtime milliseconds since 1970
device_id TEXT AWARE device UUID
esm_json TEXT the ESM questionnaire JSON string (see below)
esm_status INTEGER the status of the ESM (0-new, 1-dismissed, 2-answered, 3-expired, 4-visible, 5-branched)
esm_expiration_threshold INTEGER ESM expiration threshold (in seconds)
esm_notification_timeout INTEGER ESM notification timeout (in seconds)
double_esm_user_answer_timestamp REAL time instance of user’s answer
esm_user_answer TEXT the user’s answer
esm_trigger TEXT a string with what triggered the ESM

ESMs in practice

We have revamped how we build and queue ESMs to make it more user friendly and less error prone (moving away from having to write JSON objects!). To create an ESM queue, we now use the Factory software design pattern. Here are some examples.

Free text

This ESM allows the user to provide free text input as context. This can be leveraged to capture sensor-challenging context, such personal opinions.

To define this free text ESM:

try {
 ESMFactory factory = new ESMFactory();
 
 //define ESM question
 ESM_Freetext esmFreetext = new ESM_Freetext();
 esmFreetext.setTitle("Freetext")
 .setTrigger("an esm queue from AWARE")
 .setSubmitButton("OK")
 .setInstructions("Open-ended text input");
 
 //add them to the factory
 factory.addESM(esmFreetext);
 
 //Queue them
 ESM.queueESM(context, factory.build());
 
 } catch (JSONException e) {
 e.printStackTrace();
 }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":1,"esm_title":"Freetext","esm_trigger":"an esm queue from AWARE","esm_submit":"OK","esm_instructions":"Open-ended text input"}}

Radio

This ESM only allows the user to select a single option from a list of alternatives.

One of the options can be defined as “Other”, which will prompt the user to be more specific, replacing “Other” with the users’ defined option.

In case you wish to use a different word than “Other” you may override it on /res/values/strings.xml file on your project. To do so, add a new string resource with your new word and what appears on the title requesting the user to be more specific:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="aware_esm_other">Other</string>
<string name="aware_esm_other_follow">Can you be more specific, please?</string>
</resources>

To define this radio ESM:

try {
            ESMFactory factory = new ESMFactory();

            //define ESM question
            ESM_Radio esmRadio = new ESM_Radio();
            esmRadio.addRadio("Radio 1")
                    .addRadio("Radio 2")
                    .setTitle("Radios")
                    .setInstructions("Radios ESM")
                    .setSubmitButton("OK");

            //add them to the factory
            factory.addESM(esmRadio);

            //Queue them
            ESM.queueESM(context, factory.build());

        } catch (JSONException e) {
            e.printStackTrace();
        }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":2,"esm_radios":["Radio 1","Radio 2"],"esm_title":"Radios","esm_instructions":"Radios ESM","esm_submit":"OK"}}

Checkbox

screenshot_20161120-155907

This ESM allows the user to select one or more options from a list of alternatives. Similar to the Radio ESM, one of the options can be defined as “Other”, which will prompt the user to be more specific, replacing “Other” with the users’ defined option.

In case you wish to use a different word than “Other” you may override it on /res/values/strings.xml file on your project. To do so, add a new string resource with your new word and what appears on the title requesting the user to be more specific:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="aware_esm_other">Other</string>
<string name="aware_esm_other_follow">Can you be more specific, please?</string>
</resources>

To define this checkbox ESM:

try {
            ESMFactory factory = new ESMFactory();

            //define ESM question
            ESM_Checkbox esmCheckbox = new ESM_Checkbox();
            esmCheckbox.addCheck("Option 1")
                    .addCheck("Option 2")
                    .addCheck("Other")
                    .setTitle("Checkbox")
                    .setSubmitButton("OK")
                    .setInstructions("Multiple choice is allowed");

            //add them to the factory
            factory.addESM(esmCheckbox);

            //Queue them
            ESM.queueESM(context, factory.build());

        } catch (JSONException e) {
            e.printStackTrace();
        }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":3,"esm_checkboxes":["Option 1","Option 2","Other"],"esm_title":"Checkbox","esm_submit":"OK","esm_instructions":"Multiple choice is allowed"}}

Likert Scale

This ESM allows the user to provide ratings, between 0 and 5/7, at 0.5/1 increments. The likert scale labels are also customisable. The default rating is no rating.

To define this likert scale ESM:

try {
            ESMFactory factory = new ESMFactory();

            //define ESM question
            ESM_Likert esmLikert = new ESM_Likert();
            esmLikert.setLikertMax(5)
                    .setLikertMaxLabel("Great")
                    .setLikertMinLabel("Poor")
                    .setLikertStep(1)
                    .setTitle("Likert")
                    .setInstructions("Likert ESM")
                    .setSubmitButton("OK");

            //add them to the factory
            factory.addESM(esmLikert);

            //Queue them
            ESM.queueESM(context, factory.build());

        } catch (JSONException e) {
            e.printStackTrace();
        }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":4,"esm_likert_max":5,"esm_likert_max_label":"Great","esm_likert_min_label":"Poor","esm_likert_step":1,"esm_title":"Likert","esm_instructions":"Likert ESM","esm_submit":"OK"}}

Quick Answer

This ESM allows the user to quickly answer the ESM. The button arrangement  is fluid, to support more or less inputs. Keep in mind that a dialog can only grow as much as there is screen estate and we don’t recommend more than 6 items here.

Unlike previous ESMs , there is no “Cancel” button for this type of ESM. However, the user can dismiss the questionnaire by pressing the Home or Back button on the device.

To define this quick answer ESM:

try {
            ESMFactory factory = new ESMFactory();

            //define ESM question
            ESM_QuickAnswer esmQuickAnswer = new ESM_QuickAnswer();
            esmQuickAnswer.addQuickAnswer("Yes")
                    .addQuickAnswer("No")
                    .setInstructions("Quick Answers ESM");

            //add them to the factory
            factory.addESM(esmQuickAnswer);

            //Queue them
            ESM.queueESM(context, factory.build());

        } catch (JSONException e) {
            e.printStackTrace();
        }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":5,"esm_quick_answers":["Yes","No"],"esm_instructions":"Quick Answers ESM"}}

Scale

esm_scale_negativeesm_scale_positive

This ESM allows the user to select a value within a range of values. The range can be positive (e.g., X to Y) where X and Y are both positive numbers; or negatively balanced (e.g., -X to X), where X is the same value.

To define this scale ESM:

try {
            ESMFactory factory = new ESMFactory();
            //define ESM question
            ESM_Scale esmScale = new ESM_Scale();
            esmScale.setScaleMax(100)
            .setScaleMin(0)
            .setScaleStart(50)
            .setScaleMaxLabel("Perfect")
            .setScaleMinLabel("Poor")
            .setScaleStep(10)
            .setTitle("Scale")
            .setInstructions("Scale ESM")
            .setExpirationThreshold(0)
            .setSubmitButton("OK");

            //add them to the factory
            factory.addESM(esmScale);

            //Queue them
            ESM.queueESM(context, factory.build()); } catch (JSONException e) { e.printStackTrace(); }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":6,"esm_scale_max":100,"esm_scale_min":0,"esm_scale_start":50,"esm_scale_max_label":"Perfect","esm_scale_min_label":"Poor","esm_scale_step":10,"esm_title":"Scale","esm_instructions":"Scale ESM","esm_submit":"OK"}}

Numeric

This ESM allows the user to provide numeric input. Numbers inputted by the participant can be both full numbers and decimals.

To define this numeric ESM:

try {
            ESMFactory factory = new ESMFactory();
            //define ESM question
            ESM_Number esmNumeric = new ESM_Number();
            esmNumeric.setTitle("Numeric")
            .setInstructions("The user can enter a number")
            .setSubmitButton("OK");

            //add them to the factory
            factory.addESM(esmNumeric);

            //Queue them
            ESM.queueESM(context, factory.build()); } catch (JSONException e) { e.printStackTrace(); }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":9,"esm_title":"Numeric","esm_instructions":"The user can enter a number","esm_submit":"OK"}}

Web

This ESM allows you to ask the user to answer an online survey.

To define this ESM:

try {
            ESMFactory factory = new ESMFactory();

            ESM_Web web = new ESM_Web();
            web.setURL("https://www.google.com");
            web.setTitle("Web survey");
            web.setInstructions("Fill out this survey. Press OK when finished");
            web.setSubmitButton("OK");

            factory.addESM(web);

            ESM.queueESM(context, factory.build());

        } catch (JSONException e) {
            e.printStackTrace();
        }

Internally, AWARE defines this question as JSON:

{"esm":{"esm_type":10,"esm_title":"ESM Web","esm_instructions":"Please fill out this online survey. Press OK when done.","esm_submit":"OK","esm_url":"https://www.google.com"}}

Step-by-step ESM Questionnaire

To create a step-by-step questionnaire, we can group multiple ESM definitions together. For example, if we want to combine a free text, with a radio question, we can define them as a queue, where they are shown sequentially:

try {
        ESMFactory factory = new ESMFactory();

        ESM_Freetext esmFreetext = new ESM_Freetext();
        esmFreetext.setTitle("What is on your mind?")
        .setSubmitButton("Next")
        .setInstructions("Tell us how you feel");

        ESM_Radio esmRadio = new ESM_Radio();
        esmRadio.addRadio("Bored")
        .addRadio("Fantastic")
        .setTitle("Are you...")
        .setInstructions("Pick one!")
        .setSubmitButton("OK");

        //add them to the factory
        factory.addESM(esmFreetext);
        factory.addESM(esmRadio);

        //Queue them
        ESM.queueESM(context, factory.build()); } catch (JSONException e) { e.printStackTrace(); }

 Internally, AWARE defines this queue of questions as a JSON array:

[{"esm":{"esm_type":1,"esm_title":"What is on your mind?","esm_instructions":"Tell us how you feel","esm_submit":"Next"}},{"esm":{"esm_type":2,"esm_radios":["Bored","Fantastic"],"esm_title":"Are you...","esm_instructions":"Pick one!","esm_submit":"OK"}}]

ESM Questionnaire Flow

To make the next question displayed to the participant dependent on the participant’s answer to the current question, make use of the flow logic for ESMs. For example, to present a different question following a boolean yes/no choice, define your ESM queue like this:

try {
        ESMFactory factory = new ESMFactory();

        ESM_PAM q1 = new ESM_PAM();
        q1.setTitle("Your mood")
        .setInstructions("Choose the closest to how you feel right now.")
        .setSubmitButton("Thanks!");

        ESM_Radio q2 = new ESM_Radio();
        q2.addRadio("Eating")
        .addRadio("Working")
        .addRadio("Not alone")
        .setTitle("Why is that?")
        .setSubmitButton("Thanks!");

        ESM_QuickAnswer q0 = new ESM_QuickAnswer();
        q0.addQuickAnswer("Yes")
        .addQuickAnswer("No")
        .setTitle("Is this a good time to answer?")
        .addFlow("Yes", q1.build())
        .addFlow("No", q2.build());

        //add ONLY the last one (q0) to the factory, as the others (q1, q2) are part of the flow for Yes and No
        factory.addESM(q0);

        //Queue them
        ESM.queueESM(context, factory.build()); } catch (JSONException e) { e.printStackTrace(); }

Internally, AWARE defines this question flow as JSON:

{"esm":{"esm_type":5,"esm_quick_answers":["Yes","No"],"esm_title":"Is this a good time to answer?","esm_flows":[{"user_answer":"Yes","next_esm":{"esm":{"esm_type":8,"esm_title":"Your mood","esm_instructions":"Choose the closest to how you feel right now.","esm_submit":"Thanks!"}}},{"user_answer":"No","next_esm":{"esm":{"esm_type":2,"esm_radios":["Eating","Working","Not alone"],"esm_title":"Why is that?","esm_submit":"Thanks!"}}}]}}

icon_ios iOS

Please check a tutoral for developing ESMs on iOS or iOS ESM plugin.

ESM