Stage 5 - Signalling Schedules

Stage 5 - Customizing The Signalling Schedule

How to customize Experience Sampler with your own signalling schedule

The scheduling notification function will differ depending on the design chosen.

Research Design Decisions

    Before we begin customizing the scheduleNotifs function, a few decisions need to be made about the nature of the design:
  •      When the daily data collection period should start and stop
  •      Whether the daily data collection period will vary depending on the day (e.g., to accommodate changes in participants'
         schedules depending on whether it is a weekday or weekend)
  •      How much time should elapse between signals
  •      For signal-contingent designs with random intervals, what is the minimum and maximum amount of time that should elapse between signals
  •      For signal-contingent designs with fixed intervals, how much time should elapse between signals
  •      For interval-contingent designs, when should participants complete the questionnaire (e.g., after waking up, before bed, or after dinner)
  •      How many questionnaires participants should complete each day
  •      How long the experience sampling period is (e.g., one week, two weeks, one month)
One important caveat you need to keep in mind when making these decisions is that iOS devices can only schedule 64 unique notifications for each app. But these notifications can be scheduled to repeat every second, every minute, every hour, every day, every month, or every year, depending on how you specify the properties of the notifications (again, see this README for more information about the properties of this notification plugin). So you can program the app to schedule 64 notifications with 64 unique ids, but these notifications can be set to be repeated regularly. These are no known limits on the number of notifications that can be scheduled on Android devices.

scheduleNotif Function

Because the schedulingQuestionnaires function is crucial to the overall functioning of the app in experience sampling studies, we will describe each part of the function thoroughly so that you can customize the app to meet your study design needs.
     schedulingQuestionnaires:function() {
          var interval1, interval2, interval3, … intervalX;
          var a, b, c, …;
          var date1, date2, date3, … dateX;
          var currentMaxHour, currentMaxMinute, currentMinHour, currentMinMinute, nextMinHour, nextMinMinute;
          var weekdayWakeTime = localStore.weekdayWakeTime.split(":");
          var weekdayDinnerTime = localStore.weekdayDinnerTime.split(":");
          var weekendWakeTime = localStore.weekendWakeTime.split(":");
          var weekendDinnerTime = localStore.weekendDinnerTime.split(":");
          var dateObject = new Date();
          var now = dateObject.getTime();
          var dayOfWeek = dateObject.getDay(), currentHour = dateObject.getHours(), currentMinute = dateObject.getMinutes();
          var nightlyLag, currentLag, maxInterval, dinnerInterval;

If you are customizing an iOS version of ExperienceSampler, then you will also need to declare the next line:
          var notifs=[];

First, we declare the variables that we need to schedule the notifications.
The first line declares the number of intervals that will need to be calculated. X is the maximum number of intervals that should be calculated for each day.
The second line declares the variables that represent the ID numbers for the notifications being scheduled. This allows you to generate unique IDs for each notification so that notifications will not be overwritten. We used letters, but any name or variable can be used.
The third line declares the dates of the notifications. Again, X is the maximum number of intervals that should be calculated for each day.
The fourth line declares variables that represent the start (currentMinHour, currentMinMinute, nextMinHour, nextMinMinute) and end times
(currentMaxHour, currentMaxMinute) of the daily data collection period. If you choose not to allow the daily data collection period to vary depending
on the day of the week, you do not need to declare these variables. You can simply use the variable names used to represent the start and end times of the daily data collection period used in the participant setup questions.
Lines 5 - 8 are used to create an array to represent the participant's wake and dinner time. These variables will be used to customize the signalling schedule to the participants' idiosyncratic schedule.
Lines 9 - 11 are used to create variables to represent the time when the participant is answering the participant setup questions (i.e, the setup time), so that the time intervals calculated can be added the setup time.
The last line in this section declares variables to represent the various intervals of times needed to accurately schedule the notifications:
     nightlyLag - the amount of time between the end of the daily data collection period and the start of the next daily data collection period
     currentLag - the amount of time between the setup time and the start of the first daily data collection period
     maxInterval - the amount of time between the start and end of the daily data collection period
     dinnerInterval - the amount of time until the end time of the next daily data collection period
The additional line for the iOS version creates an empty array that will store all the properties of the notifications that we want to schedule. We will then schedule all the notifications at once.

          for (i = 0; i < X; i++) {
               var alarmDay = dayOfWeek + 1 + i;
               if (alarmDay > 6) {alarmDay = alarmDay-7;}
               if (alarmDay == 0 || alarmDay == 6) {
                    currentMaxHour = weekendDinnerTime[0];
                    currentMaxMinute = weekendDinnerTime[1];
                    currentMinHour = weekendWakeTime[0];
                    currentMinMinute = weekendWakeTime[1];
                    if (alarmDay == 0) {
                         nextMinHour = weekdayWakeTime[0];
                         nextMinMinute = weekdayWakeTime[1];
                         }
                    else {
                         nextMinHour = weekendWakeTime[0];
                         nextMinMinute = weekendWakeTime[1];
                         }
                    currentLag = (((((24 - parseInt(currentHour) + parseInt(weekendWakeTime[0]))*60) - parseInt(currentMinute) +                          parseInt(weekendWakeTime[1]))*60)*1000);
               }
               else {
                    currentMaxHour = weekdayDinnerTime[0];
                    currentMaxMinute = weekdayDinnerTime[1];
                    currentMinHour = weekdayWakeTime[0];
                    currentMinMinute = weekdayWakeTime[1];
                    if (alarmDay == 5) {
                         nextMinHour = weekendWakeTime[0];
                         nextMinMinute = weekendWakeTime[1];
                         }
                    else {
                         nextMinHour = weekdayWakeTime[0];
                         nextMinMinute = weekdayWakeTime[1];
                         }
                    currentLag = (((((24 - parseInt(currentHour) + parseInt(weekendWakeTime[0]))*60) - parseInt(currentMinute) +                          parseInt(weekendWakeTime[1];))*60)*1000);
               }
               if (alarmDay == 5 || alarmDay == 0){
                    nightlyLag = currentLag;
               }
               else {
                    nightlyLag= (((((24 - parseInt(currentHour) + parseInt(nextMinHour))*60) – parseInt(currentMinute) +
                         parseInt(nextMinMinute))*60)*1000);

               }

The first line in this section sets the parameters of the loop. X is the number of times the loop should repeat itself. X should be the number of days in the data collection period, keeping in mind that iOS apps can only store 64 unique notifications.
The first part of the code inside the loop calculates the current amount of time between the setup time and the start of the first daily data collection period as well as the time between the end of the daily data collection period and the start of the next daily data collection period. If you allow the daily data collection period to vary, you should include the conditional statements to calculate different currentLag lengths depending on the day of the week. If you choose not to allow the daily data collection period to vary, then you will you not need the conditional statements.
alarmDay is the day of the week for which the loop is scheduling the notifications
Variables that begin with localStore are the participant setup variables that indicate the start and end times of the daily data collection period.

For signal-contingent designs with random intervals, the script should randomly generate an amount of time that falls within a specific range of interval lengths.
               maxInterval = (((((parseInt(currentMaxHour) – parseInt(currentMinHour))*60) + parseInt(currentMaxMinute) –                     parseInt(currentMinMinute))*60)*1000);
               interval1 = (parseInt(nightlyLag) + parseInt(Y))*1000) + ((parseInt(86400)*parseInt(i))*1000));
               interval2 = (interval1 + ((parseInt(Math.round(Math.random()*Y)+Z))*1000));
               interval3 = (interval2 + ((parseInt(Math.round(Math.random()*Y)+Z))*1000));
               …
               intervalX = (intervalX-1 + ((parseInt(Math.round(Math.random()*Y)+Z))*1000));
               dinnerInterval = parseInt(currentLag) + parseInt(maxInterval) + (parseInt(86400000)*parseInt(i));

where
X is the number of notifications scheduled for each day
Z is the minimum amount of time in seconds that should elapse between intervals
Y is the additional time in seconds that should elapse between intervals to equal the maximum amount of time that should elapse between intervals (i.e., Y + Z = maximum amount of time between two notifications).
Y is multiplied by a randomly generated decimal to make the notifications random.
For example, if you decide that the minimum amount of time that should elapse between notifications is one hour and the maximum amount of time that should elapse between notifications is two hours, then both Y and Z should equal 3600.

For signal-contingent designs with fixed intervals, the script should add a predetermined amount of time to the previous lengths.
               interval1 = (parseInt(nightlyLag) + ((parseInt(Math.round(Math.random()*Y)+Z))*1000) + ((parseInt(86400)*parseInt(i))*1000));
               interval2 = ((interval1 + parseInt(Y))*1000);
               interval3 = ((interval2 + parseInt(Y))*1000);
               …
               intervalX = ((intervalX-1 + parseInt(Y))*1000);

where
X is the number of notifications for the day
Y is the predetermined interval of time in seconds between signals (e.g., 2 hours between each signal would be 7200 seconds).
If you choose not to allow the daily data collection period to vary depending on the day of the week, they can take advantage of the repeat property and have the notifications repeat daily, thus reducing the number of notifications that need to be scheduled.

For interval-contingent designs, you should customize the signals to particular daily activities, such as 1 hour after waking up, during lunch, after dinner, or before bed, depending on the phenomenon that you are interested in. Add these questions to your participant setup questions. Then you simply need to calculate the time between setup and these various times of interest using algorithms similar to the ones presented above. Once these intervals are calculated, they need to be added to the setup time. You can then take advantage of the repeat property and schedule the notifications to repeat daily.

The code below uses the calculated time intervals to create date objects by adding them to the setup time.

               date1 = new Date(now + interval1);
               date2 = new Date(now + interval2);
               date3 = new Date(now + interval3);
               …
               dateX = new Date(now + intervalX);

The block of code below creates unique ids for each of the notifications

               a = 101+(parseInt(i)*100);
               b = 102+(parseInt(i)*100);
               c = 103+(parseInt(i)*100);
               …

Finally, we use the block of code below to schedule all the notifications. Again, the properties specified in these lines can be changed to meet specific design restructions. Given that iOS can only schedule 64 unique. This is where the Android and iOS version differ

Android version

               cordova.plugins.notification.local.schedule([
               {icon: 'ic_launcher', id: a, at: date1, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'},
               {icon: 'ic_launcher', id: b, at: date2, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'},
               {icon: 'ic_launcher', id: c, at: date3, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'},
               {icon: 'ic_launcher', id: d, at: date4, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'},
               {icon: 'ic_launcher', id: e, at: date5, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'},
               {icon: 'ic_launcher', id: f, at: date6, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'}]);

iOS version

               notifs.push({id: a, at: date1, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'});
               notifs.push({id: b, at: date2, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'});
               notifs.push({id: c, at: date3, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'});
               notifs.push({id: d, at: date4, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'});
               notifs.push({id: e, at: date5, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'});
               notifs.push({id: f, at: date6, text: 'Time for your next Diary Survey!', title: 'Diary Surveys'});

The final block of code records when the notifications are scheduled to fire and records it in the participant's data file:
               localStore['notification_' + i + '_1'] = localStore.participant_id + "_" + a + "_" + date1;
               localStore['notification_' + i + '_2'] = localStore.participant_id + "_" + b + "_" + date2;
               localStore['notification_' + i + '_3'] = localStore.participant_id + "_" + c + "_" + date3;
               …
               }

If you are programming the iOS version, you will need to include this line before the closing bracket of this function:
               cordova.plugins.notification.local.schedule(notifs);
     }

You can use the recorded notifications to determine if there is an error in the scheduling algorithm (when programming and testing the app) and determine response latences (i.e., time between when participants are beeped and when they complete the survey). The number following "notification" indicates the interval number, and i represents the day of the experience sampling period, with 0 being the first day of the experience sampling period. It is followed by the participant's id number, the notification id number, and the date and time the notification is scheduled to fire.

Be sure that the following line in the recordResponse function has been uncommented:
     if (count == -1){app.scheduleNotifs();app.renderLastPage(lastPage[0], count);app.scheduledNotifs();}
The line above executes the scheduleNotifs() function after participants have completed the participant setup questions. If this line is commented out, the app will not schedule any notifications.

If your app has NO question logic, then only the following lines will be uncommented in the question logic portion of the recordResponse function:
     if (count == -1){app.scheduleNotifs();app.renderLastPage(lastPage[0], count);app.scheduledNotifs();}
     else if (count < surveyQuestions.length-1) {$("#question").fadeOut(400, function () {$("#question").html("");app.renderQuestion(count+1);});}
     *****MAKE SURE THE ELSE IN THE ABOVE STATEMENT IS UNCOMMENTED*****
     else {app.renderLastPage(lastPage[0], count);};

Now you're ready to test your app on real devices!

Stage 1 - Questions and Messages

Learn how to customize your survey questions, end-of-questionnaire messages, and participant setup questions.

More

Testing the App

It's important to test your app after each customization stage. Learn how to test your app on a smartphone emulator.

More

Stage 2 - Saving the Data

In this stage, you'll learn how to set up your server and app to save your questionnaire data.

More

Stage 3 - Advanced Survey Features

In this stage, you'll learn how to implement skip logic, question branching, and piped text for your questionnaire.

More

Stage 4 - Snooze Function

Implement a snooze function to remind participants to complete questionnaires at a later time if they are currently busy.

More

Stage 5 - Scheduling Notifications

Schedule your signals for either a signal-contingent design (with random or fixed intervals) or an interval-contingent design.

More