package locationClient; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Date; import java.util.Random; import java.util.TimeZone; import java.util.Timer; import java.util.TimerTask; import javax.microedition.io.Connector; import javax.microedition.io.HttpConnection; //import javax.microedition.io.file.FileConnection; //import javax.microedition.io.file.FileSystemRegistry; import javax.microedition.lcdui.Alert; import javax.microedition.lcdui.AlertType; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.DateField; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.List; import javax.microedition.lcdui.Screen; import javax.microedition.lcdui.StringItem; import javax.microedition.lcdui.TextField; import javax.microedition.midlet.MIDlet; import javax.microedition.rms.RecordEnumeration; import javax.microedition.rms.RecordStore; import javax.microedition.rms.RecordStoreException; import javax.microedition.rms.RecordStoreFullException; import javax.microedition.rms.RecordStoreNotFoundException; import javax.microedition.rms.RecordStoreNotOpenException; /** * * @author Noel Campbell * */ public class InterruptionExperiment extends MIDlet implements CommandListener {//, ProximityListener { // Main MIDP display private Display myDisplay = null; // Main screen variable private Screen mainScreen; // Main screen that just shows buttons to send or exit private Form sendScreen; // Alert screen that appears when coordinates have been sent private Alert storeAlert, sentAlert, waitAlert, landmarkAlert, monitoringAlert, recordStoreFullAlert, recordStoreNotFoundAlert, recordStoreAlert, recordStoreLoadAlert; // the "store" button used on sendScreen Command storeCommand; // the "exit" button used on the sendScreen Command exitCommand; // the "upload" button used on the sendScreen Command uploadCommand; // the "download" command is used to download new experiment messages from the server Command downloadCommand; // test command to write to a file using FileConnection API (JSR 75) Command writeCommand; // the "Monitor" enables proximty monitoring (so user will be alerted when near a landmark) Command monitorCommand; // these commands control the user interraction with the interruption messages Command okCommand, acceptCommand, rejectCommand, backCommand, yesCommand, noCommand, activityCommand, testCommand; // the "configure" command allows the user to set the phone to one of six modes (including experimenter mode) Command configureCommand; // Random number generator for latitude & longitude Random generator; // Latititude and Longitude Coordinates to be sent to the server private double lat, lon; // Altitude and accuracies of coordinates private float alt, hacc, vacc, speed; // Date/Timestamp of coordinate private Date dateAndTime; // Level of disruption specified by user private int disruptionMeasure; // URL of the server // private String url = "http://nlcamp.no-ip.org:8080/ServletExamples/interruption"; private String url = "http://mobilecollab.servehttp.com/interruptionServlet/interruption"; // RecordStore object that stores all coordinates and associtated info on // the phone private RecordStore coordinatesRecordStore, configurationRecordStore, experimentLogRecordStore, messagesRecordStore; // This RecordEnumeration object loops through the messages in the messagesRecordStore until all messages are presented // in the experiment private RecordEnumeration messagesRecordEnumeration; // Value used for proximityListeners of coordinates private final float proximityValue = 10; private final String passwordString = "2006"; // This timer controls the task of asking the user questions private Timer tm; private ProgramTimer pt; //private BeepTimer bt; private long lastTime; private List experimenterScreen, configureScreen; private Form interruptScreen1, passwordScreen, confirmationScreen, surveyForm; private List interruptScreen2, interruptScreen3; private StringItem interruptionString; private TextField passwordTextField; private StringItem statusStringItem; private DateField dateField; private TextField firstNameTextField, lastNameTextField, ageTextField, emailTextField, occupationTextField, question1TextField, question2TextField; private Date date; private String firstName, lastName, age, email, occupation, question1, question2; private final int PRE_EXPERIMENT_ACTIVITY = 0; private final int DO_NOT_DISTURB_ACTIVITY = 1; private final int PRIMARY_ACTIVITY_ONLY = 2; private final int SECONDARY_ACTIVITY_ONLY = 3; private final int BOTH_PRIMARY_AND_SECONDARY_ACTIVITIES = 4; private final int EXPERIMENTER_MODE = 0; private final int INTERRUPT_NEVER_MODE = 1; private final int INTERRUPT_DURING_PRIMARY_MODE = 2; private final int INTERRUPT_DURING_SECONDARY_MODE = 3; private final int INTERRUPT_DURING_PRIMARY_AND_SECONDARY_MODE = 4; private final int INTERRUPT_ALWAYS_MODE = 5; private final int COORDINATES_DATA_TYPE = 0; private final int NEW_ACTIVITY_DATA_TYPE = 1; private final int UPLOAD_LOG_DATA_TYPE = 2; private final int DOWNLOAD_MSGS_DATA_TYPE = 3; private int configurationMode, surveyRecordId; private int previousActivity, currentActivity, tempActivity, modeRecordID; private boolean warmupDone; private int msgNum, messageCount; private int relatedUnrelated; private Date interruptTime; private long accRejTime; private long moreInfoTime; private int moreInfoValue; private long disruptRespTime; // Constructor public InterruptionExperiment() { try { // initialize the GUI components myDisplay = Display.getDisplay(this); currentActivity = PRE_EXPERIMENT_ACTIVITY; previousActivity = PRE_EXPERIMENT_ACTIVITY; tempActivity = PRE_EXPERIMENT_ACTIVITY; msgNum = 0; relatedUnrelated = 1; statusStringItem = new StringItem("","Current Activity: "+currentActivity); sendScreen = new Form("Press \"Store\" to obtain and store GPS coordinates"); configurationRecordStore = RecordStore.openRecordStore("Configuration", true); if (configurationRecordStore.getNumRecords() < 1) { String defaultMode = "0"; configurationMode = 0; modeRecordID = configurationRecordStore.addRecord(defaultMode.getBytes(), 0, defaultMode.getBytes().length); } else { RecordEnumeration re = configurationRecordStore.enumerateRecords(null, null, false); while(re.hasNextElement()) { modeRecordID = re.nextRecordId(); configurationMode = Integer.parseInt(new String(configurationRecordStore.getRecord(modeRecordID))); } } storeCommand = new Command("STORE", Command.OK, 1); monitorCommand = new Command("MONITOR", Command.OK, 1); writeCommand = new Command("WRITE", Command.OK, 1); uploadCommand = new Command("UPLOAD", Command.OK, 1); exitCommand = new Command("EXIT", Command.EXIT, 1); configureCommand = new Command("CONFIGURE", Command.OK, 1); testCommand = new Command("TEST", Command.OK, 1); downloadCommand = new Command("DOWNLOAD", Command.OK, 1); // sendScreen.addCommand(testCommand); // sendScreen.addCommand(storeCommand); // sendScreen.addCommand(monitorCommand); // sendScreen.addCommand(uploadCommand); // sendScreen.addCommand(downloadCommand); // sendScreen.addCommand(writeCommand); sendScreen.addCommand(exitCommand); activityCommand = new Command("ACTIVITY", Command.BACK, 1); if (configurationMode == EXPERIMENTER_MODE) { sendScreen.addCommand(activityCommand); sendScreen.addCommand(uploadCommand); sendScreen.addCommand(downloadCommand); sendScreen.setTitle("Select ACTIVITY option to choose a new activity "+'\n'+"Mode: "+configurationMode); sendScreen.append(statusStringItem); // Reset server activity sendHttpPost((new Integer(PRE_EXPERIMENT_ACTIVITY)).toString(), NEW_ACTIVITY_DATA_TYPE); } else { sendScreen.setTitle("Please wait for the next interruption"+'\n'+"Mode: "+configurationMode); sendScreen.removeCommand(activityCommand); sendScreen.removeCommand(uploadCommand); sendScreen.removeCommand(downloadCommand); } sendScreen.addCommand(configureCommand); sendScreen.setCommandListener(this); //mainScreen = sendScreen; interruptScreen1 = new Form("Interruption"); interruptionString = new StringItem("Select 'ACCEPT' for More Info", ""); interruptionString.setLayout(StringItem.LAYOUT_CENTER); interruptScreen1.append(interruptionString); acceptCommand = new Command("ACCEPT", Command.OK, 1); rejectCommand = new Command("REJECT", Command.BACK, 1); interruptScreen1.addCommand(acceptCommand); interruptScreen1.addCommand(rejectCommand); interruptScreen1.setCommandListener(this); //#style custom interruptScreen2 = new List("Here is more info about.....Select a response", List.EXCLUSIVE); //#style custom interruptScreen2.append("Option 1", null); //#style custom interruptScreen2.append("Option 2", null); //#style custom interruptScreen2.append("Option 3", null); //#style custom interruptScreen2.append("Option 4", null); interruptScreen2.setSelectedIndex(0, true); okCommand = new Command("OK", Command.BACK, 1); backCommand = new Command("BACK", Command.OK, 1); interruptScreen2.addCommand(okCommand); interruptScreen2.setCommandListener(this); interruptScreen3 = new List("How disruptive was the previous message?", List.EXCLUSIVE); interruptScreen3.append("(0) - NOT AT ALL DISRUPTIVE", null); interruptScreen3.append("(1) - SLIGHTLY DISRUPTIVE", null); interruptScreen3.append("(2) - MODERATELY DISRUPTIVE", null); interruptScreen3.append("(3) - VERY DISRUPTIVE", null); interruptScreen3.append("(4) - EXTREMELY DISRUPTIVE", null); interruptScreen3.setSelectedIndex(3, true); interruptScreen3.addCommand(okCommand); interruptScreen3.setCommandListener(this); //#style custom experimenterScreen = new List("Select the new activity:", List.EXCLUSIVE); //#style custom experimenterScreen.append("(0) PRE-EXPERIMENT", null); //#style custom experimenterScreen.append("(1) DO NOT DISTURB", null); //#style custom experimenterScreen.append("(2) ENGAGED IN PRIMARY ACTIVITY & NOT IN SECONDARY ACTIVITY", null); //#style custom experimenterScreen.append("(3) ENGAGED IN SECONDARY ACTIVITY & NOT IN PRIMARY ACTIVITY", null); //#style custom experimenterScreen.append("(4) ENGAGED IN BOTH PRIMARY AND SECONDARY ACTIVITIES", null); experimenterScreen.addCommand(okCommand); experimenterScreen.addCommand(backCommand); experimenterScreen.setCommandListener(this); confirmationScreen = new Form("Are you sure you want to change the activity?"); yesCommand = new Command("YES", Command.OK, 1); noCommand = new Command("NO", Command.CANCEL, 1); confirmationScreen.addCommand(yesCommand); confirmationScreen.addCommand(noCommand); confirmationScreen.setCommandListener(this); passwordScreen = new Form("Please enter the password"); passwordTextField = new TextField("Password", "", 10, TextField.PASSWORD); passwordScreen.append(passwordTextField); passwordScreen.addCommand(okCommand); passwordScreen.addCommand(backCommand); passwordScreen.setCommandListener(this); configureScreen = new List("Please select the desired configuration mode", List.EXCLUSIVE); configureScreen.append("Mode 0 -- EXPERIMENTER", null); configureScreen.append("Mode 1 -- INTERRUPT NEVER", null); configureScreen.append("Mode 2 -- INTERRUPT WHILE ENGAGED IN PRIMARY ACTIVITY & NOT SECONDARY ACTIVITY", null); configureScreen.append("Mode 3 -- INTERRUPT WHILE ENGAGED IN SECONDARY ACTIVITY & NOT PRIMARY ACTIVITY", null); configureScreen.append("Mode 4 -- INTERRUPT WHILE ENGAGED IN BOTH PRIMARY AND SECONDARY ACTIVITIES", null); configureScreen.append("Mode 5 -- INTERRUPT ALWAYS", null); configureScreen.addCommand(okCommand); configureScreen.addCommand(backCommand); configureScreen.setCommandListener(this); surveyForm = new Form("Please enter the following information:"); dateField = new DateField("DATE:", DateField.DATE); dateField.setLayout(dateField.LAYOUT_LEFT); surveyForm.append(dateField); firstNameTextField = new TextField("FIRST:", "", 15, TextField.INITIAL_CAPS_WORD); surveyForm.append(firstNameTextField); lastNameTextField = new TextField("LAST:", "", 15, TextField.INITIAL_CAPS_WORD); surveyForm.append(lastNameTextField); ageTextField = new TextField("AGE:", "", 3, TextField.NUMERIC); surveyForm.append(ageTextField); emailTextField = new TextField("EMAIL:", "", 25, TextField.EMAILADDR); surveyForm.append(emailTextField); occupationTextField = new TextField("OCCUPATION:", "", 25, TextField.INITIAL_CAPS_WORD); surveyForm.append(occupationTextField); question1TextField = new TextField("COMPLETE THE SENTENCE: Ask not what your country can do for you; ask what you...", "", 50, TextField.ANY); surveyForm.append(question1TextField); question2TextField = new TextField("COMPLETE THE SENTENCE: That's one small step for a man, one giant leap...", "", 20, TextField.ANY); surveyForm.append(question2TextField); surveyForm.addCommand(okCommand); //surveyForm.addCommand(backCommand); surveyForm.setCommandListener(this); if (configurationMode == EXPERIMENTER_MODE) { mainScreen = sendScreen; } else { mainScreen = surveyForm; } lastTime = System.currentTimeMillis(); generator = new Random(); coordinatesRecordStore = RecordStore.openRecordStore("Coordinates", true); experimentLogRecordStore = RecordStore.openRecordStore("Log", true); messagesRecordStore = RecordStore.openRecordStore("Messages", true); messagesRecordEnumeration = messagesRecordStore.enumerateRecords(null, null, false); warmupDone = false; messageCount = 0; pt = new ProgramTimer(); //bt = new BeepTimer(); tm = new Timer(); // Set timer to go off some random time within the next minute and repeat every 30 seconds; tm.schedule(pt, Math.abs((generator.nextLong()/Long.MAX_VALUE)*60000), 30000); } catch (RecordStoreFullException e) { recordStoreFullAlert = new Alert( "RecordStore Full", "Unable to open coordinates record store because it is full", null, AlertType.INFO); recordStoreFullAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreFullAlert); } catch (RecordStoreNotFoundException e) { recordStoreNotFoundAlert = new Alert("RecordStore Not Found", "coordinates record store could not be found", null, AlertType.INFO); recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreNotFoundAlert); } catch (RecordStoreException e) { recordStoreAlert = new Alert( "RecordStore Failure", "An error occured while attempting to open the coordinates record store", null, AlertType.INFO); recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreAlert); } } public void startApp() { myDisplay.setCurrent(mainScreen); } public void commandAction(Command com, Displayable disp) { // when user clicks on the "store" button if (com == storeCommand) { // // Display status message while connecting to GPS // waitAlert = new Alert("Connecting to GPS", "Obtaining GPS Fix...", // null, AlertType.INFO); // waitAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(waitAlert); // // try { // // Set criteria for selecting a location provider: // Criteria cr = new Criteria(); // cr.setHorizontalAccuracy(1); // cr.setVerticalAccuracy(1); // // // Get an instance of the provider // LocationProvider lp = LocationProvider.getInstance(cr); // // // Request the location, setting a five-minute timeout // Location l = lp.getLocation(300); // QualifiedCoordinates qc = l.getQualifiedCoordinates(); // //LocationProvider.addProximityListener(this, qc, proximityValue); // long timestamp = l.getTimestamp(); // dateAndTime = new Date(timestamp); // speed = l.getSpeed(); // // if (qc != null) { // // Use coordinate information // lat = qc.getLatitude(); // lon = qc.getLongitude(); // alt = qc.getAltitude(); // hacc = qc.getHorizontalAccuracy(); // vacc = qc.getVerticalAccuracy(); // } // // String recordString = lat + "," + lon + "," + alt + "," + hacc // + "," + vacc + "," +speed+","+ dateAndTime.toString(); // // // Store string in coordinatesRecordStore // byte[] recordStringByteArray = recordString.getBytes(); // coordinatesRecordStore.addRecord(recordStringByteArray, 0, // recordStringByteArray.length); // // storeAlert = new Alert("Landmark Stored", // "Landmark successfully stored on phone", null, // AlertType.INFO); // storeAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(storeAlert); // // } catch (LocationException le) { // storeAlert = new Alert("GPS Error", // "Error obtaining GPS coordinates", null, AlertType.INFO); // storeAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(storeAlert); // } catch (InterruptedException ie) { // storeAlert = new Alert( // "GPS Error", // "Interruption while attempting to obtain GPS coordinates", // null, AlertType.INFO); // storeAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(storeAlert); // } catch (RecordStoreFullException e) { // recordStoreFullAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(recordStoreFullAlert); // } catch (RecordStoreException e) { // recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(recordStoreAlert); // } } else if (com == monitorCommand) { // // Add a proximityListener for each stored coordinate // String msg = "Proximity monitoring has been enabled for "+loadStoredCoordinates(coordinatesRecordStore)+" landmarks!"; // Alert recordsLoadedAlert = new Alert("Proximity Monitoring Enabled", msg, null, AlertType.CONFIRMATION); // recordsLoadedAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(recordsLoadedAlert); // } else if (com == uploadCommand) { try { //String msg = sendHttpPost(); String msg = "Size of RecordStore: "+experimentLogRecordStore.getNumRecords(); msg = msg+" iterations: "+uploadRecords(experimentLogRecordStore); // uploadRecords(coordinatesRecordStore); sentAlert = new Alert("Experiment Log Entries Sent", msg, null, AlertType.INFO); sentAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(sentAlert); } catch (RecordStoreNotOpenException e) { Alert recordStoreUploadAlert = new Alert(e.toString(), "Eror uploading log to server", null, AlertType.INFO); recordStoreUploadAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreUploadAlert); } } else if (com == downloadCommand) { String msg = "Total number of messages downloaded from server: "+downloadMessages(); sentAlert = new Alert("Interruption Messages Downloaded", msg, null, AlertType.INFO); sentAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(sentAlert); } else if (com == writeCommand) { try { String testString = "test"+'\n'; lat = 0; lon = 0; alt = 1; hacc = 1; vacc = 1; speed = 2; String recordString = lat + "," + lon + "," + alt + "," + hacc+ "," + vacc + ","+speed+"," + "date"; // writeCoordinates(testString); // Store string in coordinatesRecordStore byte[] recordStringByteArray = recordString.getBytes(); coordinatesRecordStore.addRecord(recordStringByteArray, 0, recordStringByteArray.length); storeAlert = new Alert("Landmark Stored", "Landmark successfully stored on phone", null, AlertType.INFO); storeAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(storeAlert); } catch (RecordStoreFullException e) { recordStoreFullAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreFullAlert); } catch (RecordStoreException e) { recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreAlert); } } else if (com == exitCommand) { // LocationProvider.removeProximityListener(this); destroyApp(true); notifyDestroyed(); } else if (com == testCommand) { String serverString = sendHttpPost("2", NEW_ACTIVITY_DATA_TYPE); sendScreen.setTitle(serverString); sendScreen.deleteAll(); int serverActivity = sendHttpGet(); statusStringItem.setText("Server Activity: "+serverActivity); sendScreen.append(statusStringItem); mainScreen = sendScreen; myDisplay.setCurrent(mainScreen); } else if (com == configureCommand) { mainScreen = passwordScreen; myDisplay.setCurrent(mainScreen); } else if (com == backCommand) { passwordTextField.setString(""); mainScreen = sendScreen; myDisplay.setCurrent(mainScreen); } else if (com == activityCommand) { mainScreen = experimenterScreen; experimenterScreen.setSelectedIndex(currentActivity, true); myDisplay.setCurrent(mainScreen); } else if (com == yesCommand) { if (disp == confirmationScreen) { mainScreen = sendScreen; myDisplay.setCurrent(mainScreen); // Alert the server of the new activity if (sendHttpPost((new Integer(tempActivity)).toString(), NEW_ACTIVITY_DATA_TYPE).equals("success")) { previousActivity = currentActivity; currentActivity = tempActivity; statusStringItem.setText("Previous Activity: "+previousActivity+'\n'+"Current Activity: "+currentActivity); } } } else if (com == noCommand) { if (disp == confirmationScreen) { mainScreen = sendScreen; myDisplay.setCurrent(mainScreen); } } else if (com == okCommand) { if (disp == surveyForm) { date = dateField.getDate(); firstName = firstNameTextField.getString().trim(); lastName = lastNameTextField.getString().trim(); age = ageTextField.getString().trim(); email = emailTextField.getString().trim(); occupation = occupationTextField.getString().trim(); question1 = question1TextField.getString().trim(); question2 = question2TextField.getString().trim(); try { String logString = "Date: "+date.toString()+", First: "+firstName+", Last: "+lastName+", Age: "+age+", Email: "+email+ ", Occupation: "+occupation+", Quote 1: "+question1+", Quote 2: "+question2+ ", Entry time: "+(System.currentTimeMillis()-lastTime); surveyRecordId = experimentLogRecordStore.addRecord(logString.getBytes(), 0, logString.getBytes().length); } catch (RecordStoreFullException e) { recordStoreFullAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreFullAlert); } catch (RecordStoreException e) { recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreAlert); } mainScreen = sendScreen; myDisplay.setCurrent(mainScreen); } else if (disp == interruptScreen2) { int selectedItem = interruptScreen2.getSelectedIndex(); if (selectedItem == -1) { mainScreen = interruptScreen3; myDisplay.setCurrent(mainScreen); } else { myDisplay.vibrate(0); myDisplay.flashBacklight(0); moreInfoTime = System.currentTimeMillis() - lastTime; moreInfoValue = selectedItem; lastTime = System.currentTimeMillis(); mainScreen = interruptScreen3; myDisplay.setCurrent(mainScreen); } } else if (disp == interruptScreen3) { int selectedItem = interruptScreen3.getSelectedIndex(); if (selectedItem == -1) { // if no selection, don't change the screen mainScreen = interruptScreen3; myDisplay.setCurrent(mainScreen); } else { disruptionMeasure = selectedItem; disruptRespTime = System.currentTimeMillis() - lastTime; String logString = "First: "+firstName+", Last: "+lastName+", Curr. Activity: "+currentActivity+", Prev. Activity: " +previousActivity+", Config. Mode: "+configurationMode+", Msg Num: "+msgNum+", Related Msg?: "+relatedUnrelated+ ", Interrupt Time: "+interruptTime.toString()+", Acc/Rej Time: "+accRejTime+", More Info Time: "+moreInfoTime+ ", More Info Val: "+moreInfoValue+", Disrupt Resp. Time: "+disruptRespTime+", Disruption Measure: "+disruptionMeasure; try { experimentLogRecordStore.addRecord(logString.getBytes(), 0, logString.getBytes().length); } catch (RecordStoreFullException e) { recordStoreFullAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreFullAlert); } catch (RecordStoreException e) { recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreAlert); } mainScreen = sendScreen; myDisplay.setCurrent(mainScreen); } } else if (disp == passwordScreen) { if (passwordTextField.getString().equals(passwordString)) { passwordTextField.setString(""); mainScreen = configureScreen; myDisplay.setCurrent(mainScreen); } else { passwordTextField.setString(""); mainScreen = passwordScreen; myDisplay.setCurrent(mainScreen); } } else if (disp == experimenterScreen) { int selectedItem = experimenterScreen.getSelectedIndex(); switch(selectedItem){ case 0: tempActivity = PRE_EXPERIMENT_ACTIVITY; break; case 1: tempActivity = DO_NOT_DISTURB_ACTIVITY; break; case 2: tempActivity = PRIMARY_ACTIVITY_ONLY; break; case 3: tempActivity = SECONDARY_ACTIVITY_ONLY; break; case 4: tempActivity = BOTH_PRIMARY_AND_SECONDARY_ACTIVITIES; break; default: tempActivity = currentActivity; break; } mainScreen = confirmationScreen; myDisplay.setCurrent(mainScreen); } else if (disp == configureScreen) { int selectedItem = configureScreen.getSelectedIndex(); try { switch(selectedItem) { case 0: configurationMode = EXPERIMENTER_MODE; mainScreen = sendScreen; break; case 1: if (configurationMode!=EXPERIMENTER_MODE) { experimentLogRecordStore.deleteRecord(surveyRecordId); } mainScreen = surveyForm; configurationMode = INTERRUPT_NEVER_MODE; break; case 2: if (configurationMode!=EXPERIMENTER_MODE) { experimentLogRecordStore.deleteRecord(surveyRecordId); } mainScreen = surveyForm; configurationMode = INTERRUPT_DURING_PRIMARY_MODE; break; case 3: if (configurationMode!=EXPERIMENTER_MODE) { experimentLogRecordStore.deleteRecord(surveyRecordId); } mainScreen = surveyForm; configurationMode = INTERRUPT_DURING_SECONDARY_MODE; break; case 4: if (configurationMode!=EXPERIMENTER_MODE) { experimentLogRecordStore.deleteRecord(surveyRecordId); } mainScreen = surveyForm; configurationMode = INTERRUPT_DURING_PRIMARY_AND_SECONDARY_MODE; break; case 5: if (configurationMode!=EXPERIMENTER_MODE) { experimentLogRecordStore.deleteRecord(surveyRecordId); } mainScreen = surveyForm; configurationMode = INTERRUPT_ALWAYS_MODE; break; default: mainScreen = configureScreen; break; } if (configurationMode == EXPERIMENTER_MODE) { sendScreen.addCommand(activityCommand); sendScreen.addCommand(uploadCommand); sendScreen.addCommand(downloadCommand); sendScreen.deleteAll(); statusStringItem.setText("Current Activity: "+currentActivity); sendScreen.append(statusStringItem); sendScreen.setTitle("Select ACTIVITY option to choose a new activity "+'\n'+"Mode: "+configurationMode); } else { sendScreen.removeCommand(activityCommand); sendScreen.removeCommand(uploadCommand); sendScreen.removeCommand(downloadCommand); sendScreen.deleteAll(); sendScreen.setTitle("Please wait for the next interruption"+'\n'+"Mode: "+configurationMode); statusStringItem.setText("Current Activity: "+currentActivity); sendScreen.append(statusStringItem); } lastTime = System.currentTimeMillis(); myDisplay.setCurrent(mainScreen); Integer c_mode = new Integer(configurationMode); configurationRecordStore.setRecord(modeRecordID, c_mode.toString().getBytes(), 0, c_mode.toString().getBytes().length); } catch (RecordStoreFullException e) { recordStoreFullAlert = new Alert( "RecordStore Full", "Unable to open configuration record store because it is full", null, AlertType.INFO); recordStoreFullAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreFullAlert); } catch (RecordStoreNotFoundException e) { recordStoreNotFoundAlert = new Alert("RecordStore Not Found", "configuration record store could not be found", null, AlertType.INFO); recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreNotFoundAlert); } catch (RecordStoreException e) { recordStoreAlert = new Alert( "RecordStore Failure", "An error occured while attempting to open the configuration record store", null, AlertType.INFO); recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreAlert); } } } else if (com == acceptCommand) { //bt.cancel(); myDisplay.vibrate(0); myDisplay.flashBacklight(0); accRejTime = System.currentTimeMillis() - lastTime; lastTime = System.currentTimeMillis(); mainScreen = interruptScreen2; myDisplay.setCurrent(mainScreen); } else if (com == rejectCommand) { //bt.cancel(); myDisplay.vibrate(0); myDisplay.flashBacklight(0); accRejTime = System.currentTimeMillis() - lastTime; moreInfoTime = -1; moreInfoValue = -1; lastTime = System.currentTimeMillis(); mainScreen = interruptScreen3; myDisplay.setCurrent(mainScreen); } } public void pauseApp() { // myDisplay.setCurrent(mainScreen); } public void destroyApp(boolean b) { // help Garbage Collector myDisplay = null; sendScreen = null; sentAlert = null; } /** * This method is called automatically when the user goes within a * pre-specified proximity of a saved landmark. The method alerts the user * that they are close to the landmark. * */ // public void proximityEvent(Coordinates coordinates, Location location) { // // Vibrate and display message if coordinates are within specified // // proximity of another stored coordinate // myDisplay.vibrate(3000); // landmarkAlert = new Alert("Landmark Reminder", // "The current location is within " + proximityValue // + " meters of a previous landmark", null, // AlertType.INFO); // landmarkAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(landmarkAlert); // } /** * This method alerts the user when the monitoring state changes */ public void monitoringStateChanged(boolean isMonitoringActive) { if (isMonitoringActive) { monitoringAlert = new Alert("Monitoring State Changed", "Proximity monitoring is now active", null, AlertType.INFO); monitoringAlert.setTimeout(5000); myDisplay.setCurrent(monitoringAlert); } else { monitoringAlert = new Alert("Monitoring State Changed", "Proximity monitoring is no longer active", null, AlertType.INFO); monitoringAlert.setTimeout(5000); myDisplay.setCurrent(monitoringAlert); } } /* * This method loads the coordinates stored in RecordStore rs and adds a * proximityListener for each coordinate */ // private int loadStoredCoordinates(RecordStore rs) { // // Get each record in RecordStore and parse to obtain lat, lon, alt, // // hacc, vacc, etc. // // Then use these values to create a new QualifiedCoordinates object for // // each record // int numRecords = 0; // try { // String rec; // double latitude, longitude; // float altitude, horizAcc, vertAcc; // int currentPosition = 0, nextPosition, i; // RecordEnumeration re = coordinatesRecordStore.enumerateRecords(null, null, false); // while(re.hasNextElement()) { // i = re.nextRecordId(); // rec = new String(coordinatesRecordStore.getRecord(i)); // currentPosition = 0; // nextPosition = rec.indexOf(','); // latitude = Double.parseDouble(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // longitude = Double.parseDouble(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // altitude = Float.parseFloat(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // horizAcc = Float.parseFloat(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // vertAcc = Float.parseFloat(rec.substring(currentPosition, // nextPosition)); // QualifiedCoordinates q = new QualifiedCoordinates(latitude, // longitude, altitude, horizAcc, vertAcc); // LocationProvider.addProximityListener(this, q, proximityValue); // numRecords += 1; // } // return numRecords; // } catch (Exception e) { // recordStoreLoadAlert = new Alert(e.toString(), // e.toString()+'\n'+"Eror loading coordinates from record store", null, // AlertType.INFO); // recordStoreLoadAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(recordStoreLoadAlert); // } // // return numRecords; // } /** * Sends each string in RecordStore (one at a time) as a HttpPost request to the server * */ private int uploadRecords(RecordStore rs) { // Get each record in RecordStore and parse to obtain lat, lon, alt, // hacc, vacc, etc. // Then use these values to send an http POST request to the server int numRecords = 0; try { String rec; int currentPosition = 0, nextPosition, i; RecordEnumeration re = rs.enumerateRecords(null, null, false); while(re.hasNextElement()) { i = re.nextRecordId(); rec = new String(rs.getRecord(i)); // currentPosition = 0; // nextPosition = rec.indexOf(','); // lat = Double.parseDouble(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // lon = Double.parseDouble(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // alt = Float.parseFloat(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // hacc = Float.parseFloat(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition + 1; // nextPosition = rec.indexOf(',', currentPosition); // vacc = Float.parseFloat(rec.substring(currentPosition, // nextPosition)); // currentPosition = nextPosition+1; // nextPosition = rec.length(); // Make httpPost reqeust to server with values retrieved from recordStore System.out.println(sendHttpPost(rec+'\n', UPLOAD_LOG_DATA_TYPE)); rs.deleteRecord(i); numRecords += 1; } return numRecords; } catch (Exception e) { recordStoreLoadAlert = new Alert(e.toString(), "Eror uploading coordinates to server"+'\n' +"numRecords: "+numRecords+'\n' , null, AlertType.INFO); recordStoreLoadAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreLoadAlert); } return -1; } /** * This method downloads all of the experiment interruption messages from the server * and stores them in the messagesRecordStore. * * @return total number of messages downloaded * -1 if there was an error downloading * */ private int downloadMessages() { int i; int numMessages = 0; try { // First delete all the current records in the record store pt.cancel(); RecordEnumeration re = messagesRecordStore.enumerateRecords(null, null, false); while(re.hasNextElement()) { i = re.nextRecordId(); messagesRecordStore.deleteRecord(i); } // Download new messages and add to the record store String msg = sendHttpPost("",DOWNLOAD_MSGS_DATA_TYPE); messagesRecordEnumeration.reset(); messagesRecordEnumeration.rebuild(); messageCount = 0; // Set timer to go off some random time within the next minute and repeat every 30 seconds; pt = new ProgramTimer(); tm.schedule(pt, Math.abs((generator.nextLong()/Long.MAX_VALUE)*60000), 30000); numMessages = messagesRecordStore.getNumRecords(); return numMessages; } catch (Exception e) { e.printStackTrace(); } return -1; } /** * * This method appends str to the end of the file landmarks.txt using the FileConnection API * (Many phones do not support this API) * */ // private void writeCoordinates(String str) { // FileConnection fcon = null; // OutputStream os = null; // // try { //// Enumeration fsrEnum = FileSystemRegistry.listRoots(); //// while (fsrEnum.hasMoreElements()) { //// String root = (String) fsrEnum.nextElement(); //// Alert rootAlert = new Alert("Root", root, null, AlertType.INFO); //// rootAlert.setTimeout(Alert.FOREVER); //// myDisplay.setCurrent(rootAlert); //// } // fcon = (FileConnection) Connector.open("file:///Phone/landmarks.txt", Connector.READ_WRITE); // if (!fcon.exists()) { // fcon.create(); // } // os = fcon.openOutputStream(fcon.fileSize()); // os.write(str.getBytes(), 0, str.getBytes().length); // // os.flush(); // os.close(); //flush and close output stream // fcon.close(); // // Alert doneWritingAlert = new Alert("Done", "The coordinates have been successfully written to landmarks.txt", null, AlertType.INFO); // doneWritingAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(doneWritingAlert); // } catch (IOException e) { // Alert ioExceptionAlert = new Alert(e.toString(), "Error writing to file landmarks.txt", null, AlertType.INFO); // ioExceptionAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(ioExceptionAlert); // } catch (SecurityException e) { // Alert securityExceptionAlert = new Alert(e.getMessage(), "SecurityException"+ // FileSystemRegistry.listRoots(), null, AlertType.INFO); // securityExceptionAlert.setTimeout(Alert.FOREVER); // myDisplay.setCurrent(securityExceptionAlert); // } // } /** * Sends an HttpGet request to the server * * @return the int response from the server * -1 or -2 if there was an error */ private int sendHttpGet() { HttpConnection hcon = null; DataInputStream dis = null; int response = -2; try { hcon = (HttpConnection) Connector.open(url, Connector.READ); hcon.setRequestMethod(HttpConnection.GET); dis = hcon.openDataInputStream(); response = dis.readInt(); } catch (IOException ioe) { ioe.printStackTrace(); } finally { try { hcon.close(); dis.close(); } catch (Exception e) { e.printStackTrace(); } } return response; } /** * Sends an HttpPost request to the server specified by the url field * * @return the String response from the server * @effects if dataType is DOWNLOAD_MSGS_DATA_TYPE, this method adds messages * to the messages record store object */ private String sendHttpPost(String requestString, int dataType) { HttpConnection hcon = null; DataInputStream dis = null; DataOutputStream dos = null; byte[] responseByteArray = new byte[20]; StringBuffer responseMessage = new StringBuffer(); try { hcon = (HttpConnection) Connector.open(url, Connector.READ_WRITE); hcon.setRequestMethod(HttpConnection.POST); dos = hcon.openDataOutputStream(); dos.writeInt(dataType); if (dataType == UPLOAD_LOG_DATA_TYPE) { dos.writeUTF(requestString); dis = hcon.openDataInputStream(); dis.read(responseByteArray); } else if (dataType == NEW_ACTIVITY_DATA_TYPE) { dos.writeInt(Integer.parseInt(requestString)); dis = hcon.openDataInputStream(); dis.read(responseByteArray); } else if (dataType == DOWNLOAD_MSGS_DATA_TYPE) { dis = hcon.openDataInputStream(); while (dis.available() > 0) { String msg = dis.readUTF(); messagesRecordStore.addRecord(msg.getBytes(), 0, msg.getBytes().length); } responseByteArray = (new String("success")).getBytes(); } responseMessage.append(new String(responseByteArray)); } catch (Exception e) { e.printStackTrace(); responseMessage.append("I/O ERROR"); } finally { // free up i/o streams and http connection try { if (hcon != null) hcon.close(); if (dis != null) dis.close(); if (dos != null) dos.close(); } catch (IOException ioe) { ioe.printStackTrace(); System.out.println(ioe); } } return responseMessage.toString().trim(); } /** * * This class controls the timer that constantly polls the server to check if a new activity * has been set. In the event of a new activity the user is interrupted (depending on the user * configuration). * */ private class ProgramTimer extends TimerTask { public final void run() { int serverActivity = sendHttpGet(); if ((serverActivity != currentActivity) && (serverActivity > PRE_EXPERIMENT_ACTIVITY) && (configurationMode != EXPERIMENTER_MODE)) { // Assume user ignored the previous message and log default values as response String str1 = ((mainScreen == interruptScreen2) || (mainScreen == interruptScreen3)) ? (new Long(accRejTime)).toString() : "NO RESPONSE"; String str2 = (mainScreen == interruptScreen3) ? (new Long(moreInfoTime)).toString() : "NO RESPONSE"; String str3 = (mainScreen == interruptScreen3) ? (new Long(moreInfoValue)).toString() : "NO RESPONSE"; String str4 = "NO RESPONSE"; String str5 = (new Integer(4)).toString(); // log highest disruption if no response to last message if ((mainScreen == interruptScreen1) || (mainScreen == interruptScreen2) || (mainScreen == interruptScreen3)) { try { String logString = "First: "+firstName+", Last: "+lastName+", Curr. Activity: "+currentActivity+", Prev. Activity: " +previousActivity+", Config. Mode: "+configurationMode+", Msg Num: "+msgNum+", Related Msg?: "+relatedUnrelated+ ", Interrupt Time: "+interruptTime.toString()+", Acc/Rej Time: "+str1+", More Info Time: "+str2+ ", More Info Val: "+str3+", Disrupt Resp. Time: "+str4+", Disruption Measure: "+str5; experimentLogRecordStore.addRecord(logString.getBytes(), 0, logString.getBytes().length); mainScreen = sendScreen; } catch (RecordStoreFullException e) { recordStoreFullAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreFullAlert); } catch (RecordStoreException e) { recordStoreNotFoundAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(recordStoreAlert); } } previousActivity = currentActivity; currentActivity = serverActivity; if (configurationMode==EXPERIMENTER_MODE) { sendScreen.setTitle("Select ACTIVITY option to choose a new activity "+'\n'+"Mode: "+configurationMode); } else { sendScreen.setTitle("Please wait for the next interruption"+'\n'+"Mode: "+configurationMode); } sendScreen.deleteAll(); statusStringItem.setText("Current Activity: "+currentActivity); sendScreen.append(statusStringItem); // Setup interruption messages by parsing the next record store message if (messagesRecordEnumeration.hasNextElement()) { try { // Check if next record is first message of experiment (only check while in warmup mode) if (!warmupDone) { int nextId = messagesRecordEnumeration.nextRecordId(); String nextRec = new String(messagesRecordStore.getRecord(nextId)); int nextMsgNum = Integer.parseInt(nextRec.substring(0,nextRec.indexOf('\t'))); if (nextMsgNum >= 0) { warmupDone = true; } // Return enumeration pointer to previous record if (messageCount > 0) { messagesRecordEnumeration.previousRecordId(); } else { messagesRecordEnumeration.reset(); } messageCount++; } // Interrupt with next message if user's configuration mode mathces the new activity OR if the message is a warmup message if (((configurationMode==INTERRUPT_ALWAYS_MODE) && (currentActivity != PRE_EXPERIMENT_ACTIVITY) && (currentActivity != DO_NOT_DISTURB_ACTIVITY)) || ((configurationMode==INTERRUPT_DURING_PRIMARY_MODE) && (currentActivity==PRIMARY_ACTIVITY_ONLY)) || ((configurationMode==INTERRUPT_DURING_SECONDARY_MODE) && (currentActivity==SECONDARY_ACTIVITY_ONLY)) || ((configurationMode==INTERRUPT_DURING_PRIMARY_AND_SECONDARY_MODE) && (currentActivity==BOTH_PRIMARY_AND_SECONDARY_ACTIVITIES)) || (!warmupDone)) { int id = messagesRecordEnumeration.nextRecordId(); // Begin parsing record string String rec = new String(messagesRecordStore.getRecord(id)); int currentPosition = 0; int nextPosition = rec.indexOf('\t'); msgNum = Integer.parseInt(rec.substring(currentPosition, nextPosition)); currentPosition = nextPosition + 1; nextPosition = rec.indexOf('\t', currentPosition); relatedUnrelated = Integer.parseInt(rec.substring(currentPosition, nextPosition)); // Set the text for the brief interrupt message on the first screen currentPosition = nextPosition + 1; nextPosition = rec.indexOf('\t', currentPosition); String screen1Message = rec.substring(currentPosition, nextPosition); interruptionString.setLabel(screen1Message); interruptionString.setText('\n'+"Press 'ACCEPT' for additional information."); // Set the text for the more info screen currentPosition = nextPosition + 1; nextPosition = rec.indexOf('\t', currentPosition); boolean noOptions = false; if (nextPosition == -1) { noOptions = true; nextPosition = rec.indexOf(';', currentPosition); } String moreInfoMessage = rec.substring(currentPosition, nextPosition); interruptScreen2.setTitle(moreInfoMessage); // Add options to the more info screen interruptScreen2.deleteAll(); currentPosition = nextPosition + 1; nextPosition = rec.indexOf('\t', currentPosition); while ((nextPosition <= rec.length()) && (nextPosition != -1)) { String option = rec.substring(currentPosition, nextPosition); interruptScreen2.append(option, null); currentPosition = nextPosition+1; nextPosition = rec.indexOf('\t', currentPosition); } if ((currentPosition < rec.length()-2) && !noOptions) { nextPosition = rec.length() - 1; String lastOption = rec.substring(currentPosition, nextPosition); interruptScreen2.append(lastOption, null); interruptScreen2.setSelectedIndex(0, true); //Default setting is for 1st Option } interruptScreen3.setSelectedIndex(2, true); //Default setting is for Moderately Disruptive mainScreen = interruptScreen1; lastTime = System.currentTimeMillis(); interruptTime = new Date(lastTime); AlertType.ALARM.playSound(myDisplay); myDisplay.vibrate(10000); myDisplay.flashBacklight(20000); } } catch (Exception e) { Alert messageLoadAlert = new Alert(e.toString(), "Eror loading interruption message from messagesRecordStore", null, AlertType.INFO); recordStoreLoadAlert.setTimeout(Alert.FOREVER); myDisplay.setCurrent(messageLoadAlert); } } else { System.out.println("End of messagesRecordEnumeration"); pt.cancel(); sendScreen.deleteAll(); //statusStringItem.setText("Last Activity: "+currentActivity+'\n'+"EXPERIMENT FINISHED"); //sendScreen.append(statusStringItem); mainScreen = sendScreen; } myDisplay.setCurrent(mainScreen); } } } private class BeepTimer extends TimerTask { int count; // cause app to beep and vibrate for 5 seconds (repeat a maximum of 3 times) public final void run() { if (count < 3) { for (int i=0; i<5; i++) { AlertType.ALARM.playSound(myDisplay); myDisplay.vibrate(1000); } count++; } else { count = 0; cancel(); } } } }