changeset 1:66b3a838feca logging tip

Added logging of user interaction
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Tue, 12 Feb 2013 15:31:48 +0000
parents e0ee6ac3a45f
children
files res/raw/cancel.mp3 src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleCheckbox.java src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleDialogBuilder.java src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleSpinner.java src/uk/ac/qmul/eecs/ccmi/accessibility/LayoutSonifier.java src/uk/ac/qmul/eecs/ccmi/activities/AccessibleActivity.java src/uk/ac/qmul/eecs/ccmi/activities/CcmiEditorAppActivity.java src/uk/ac/qmul/eecs/ccmi/activities/TreeNavigation.java src/uk/ac/qmul/eecs/ccmi/utilities/ILogger.java
diffstat 9 files changed, 208 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
Binary file res/raw/cancel.mp3 has changed
--- a/src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleCheckbox.java	Thu Dec 13 20:00:21 2012 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleCheckbox.java	Tue Feb 12 15:31:48 2013 +0000
@@ -19,6 +19,7 @@
 package uk.ac.qmul.eecs.ccmi.accessibility;
 
 import uk.ac.qmul.eecs.ccmi.activities.R;
+import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -100,6 +101,7 @@
 			@Override
 			public void onClick(View view) {
 				spinner.setSelection((spinner.getSelectedItemPosition()+1) % spinner.getCount());
+				ILogger.logTap("checkbox (new value="+spinner.getSelectedItem().toString()+')');
 				if(service != null){
 					service.speak(spinner.getSelectedItem().toString()+(checks[spinner.getSelectedItemPosition()] ? " " : " un")+"checked");
 				}
@@ -118,7 +120,8 @@
 				}
 				if(service != null)
 					service.speak(spinner.getSelectedItem().toString()+(checks[spinner.getSelectedItemPosition()] ? "" : "un")+"checked");
-				
+				ILogger.log("user long tap: spinner");
+				ILogger.log(spinner.getSelectedItem().toString()+' '+(checks[spinner.getSelectedItemPosition()] ? "" : "un")+"checked");
 				spinner.setAdapter(new ArrayAdapter<String>(getContext(),R.layout.list_item_1,markedValues));
 				spinner.setSelection(itemPosition);
 				return true;
--- a/src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleDialogBuilder.java	Thu Dec 13 20:00:21 2012 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleDialogBuilder.java	Tue Feb 12 15:31:48 2013 +0000
@@ -20,6 +20,7 @@
 
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent;
 import uk.ac.qmul.eecs.ccmi.activities.R;
+import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
@@ -83,6 +84,8 @@
         args.putInt("dialogType", type);
         args.putString("tag", tag);
 		accessibleDialogFragment.setArguments(args);
+		
+		ILogger.logDialog(tag);
 		/* show the dialog */
 		accessibleDialogFragment.show(fragmentManager,tag);
 	}
@@ -110,6 +113,8 @@
         if(text != null)
         	args.putString("text", text);
 		accessibleDialogFragment.setArguments(args);
+		
+		ILogger.logDialog(tag);
 		/* show the dialog */
 		accessibleDialogFragment.show(fragmentManager,tag);
 	}
@@ -136,6 +141,8 @@
         args.putInt("dialogType", R.layout.selection_dialog);
         args.putString("tag", tag);
 		accessibleDialogFragment.setArguments(args);
+		
+		ILogger.logDialog(tag);
 		/* show the dialog */
 		accessibleDialogFragment.show(fragmentManager,tag);
 	}
@@ -165,6 +172,8 @@
         args.putInt("dialogType", R.layout.checkbox_dialog);
         args.putString("tag", tag);
 		accessibleDialogFragment.setArguments(args);
+		
+		ILogger.logDialog(tag);
 		/* show the dialog */
 		accessibleDialogFragment.show(fragmentManager,tag);
 	}
@@ -261,6 +270,12 @@
 			LayoutSonifier.getInstance().onTouch(layout, evt, accessibilityService);
 			return super.dispatchTouchEvent(evt);
 		}
+		
+		@Override
+		public void	onBackPressed(){
+			super.onBackPressed();
+			ILogger.logTap("back (cancel)");
+		}
 	}
 	
 }
--- a/src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleSpinner.java	Thu Dec 13 20:00:21 2012 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleSpinner.java	Tue Feb 12 15:31:48 2013 +0000
@@ -18,6 +18,7 @@
 */
 package uk.ac.qmul.eecs.ccmi.accessibility;
 
+import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -66,6 +67,7 @@
 			if(service != null){
 				service.speak(getSelectedItem().toString());
 			}
+			ILogger.logTap("spinner (new value=("+getSelectedItem().toString()+')');
 		}
 		
 		return true;
--- a/src/uk/ac/qmul/eecs/ccmi/accessibility/LayoutSonifier.java	Thu Dec 13 20:00:21 2012 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/accessibility/LayoutSonifier.java	Tue Feb 12 15:31:48 2013 +0000
@@ -18,6 +18,7 @@
 */
 package uk.ac.qmul.eecs.ccmi.accessibility;
 
+import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -104,19 +105,24 @@
 			accessibilityService.vibrate();
 			CharSequence contentDescription = v.getContentDescription();
 			/* if the view has accessibility content description set, use it */
-			if(contentDescription != null )
+			if(contentDescription != null ){
 				accessibilityService.speak(contentDescription.toString() + 
 						((v instanceof TextView) ? ((TextView)v).getText() : "") );
-			else
+				ILogger.logHover(contentDescription.toString());
+			}else{
 				accessibilityService.speak(((TextView)v).getText().toString());
+				ILogger.logHover(((TextView)v).getText().toString());
+			}
 		}else if(v instanceof AccessibleCheckbox){
 			AccessibleCheckbox ab = (AccessibleCheckbox)v;
 			boolean isChecked = ab.getChecks()[ab.getSelectedValuePosition()];
 			accessibilityService.vibrate();
 			accessibilityService.speak(ab.getSelectedValue()+ (isChecked ? ", checked" : ", unchecked"));
+			ILogger.logHover("check box");
 		}else if (v instanceof Spinner){
 			accessibilityService.vibrate();
 			accessibilityService.speak(v.getContentDescription()+" "+((Spinner)v).getSelectedItem());
+			ILogger.logHover("spinner");
 		}
 	}
 	
--- a/src/uk/ac/qmul/eecs/ccmi/activities/AccessibleActivity.java	Thu Dec 13 20:00:21 2012 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/activities/AccessibleActivity.java	Tue Feb 12 15:31:48 2013 +0000
@@ -22,6 +22,7 @@
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent;
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleDialogBuilder;
 import uk.ac.qmul.eecs.ccmi.accessibility.LayoutSonifier;
+import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
 import android.content.pm.PackageManager;
 import android.media.AudioManager;
 import android.os.Bundle;
@@ -31,6 +32,7 @@
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
+import android.widget.AbsListView;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.ListAdapter;
@@ -102,6 +104,26 @@
 			}
 		};
 		list.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+		list.setOnScrollListener(new AbsListView.OnScrollListener(){
+
+			@Override
+			public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
+
+			@Override
+			public void onScrollStateChanged(AbsListView view, int scrollState) {
+				switch(scrollState){
+				case AbsListView.OnScrollListener.SCROLL_STATE_FLING : 
+					ILogger.log("user scroll: fling");
+					break;
+				case AbsListView.OnScrollListener.SCROLL_STATE_IDLE :
+					ILogger.log("user scroll: "+ view.getItemAtPosition(list.getFirstVisiblePosition()));
+					break;
+				case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL :
+					ILogger.log("user scroll: started scrolling");
+					break;	
+				}
+			}
+		});
 		listLayout.addView(list);
 	
 		/* init header with a button to scroll down an up the list */
--- a/src/uk/ac/qmul/eecs/ccmi/activities/CcmiEditorAppActivity.java	Thu Dec 13 20:00:21 2012 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/activities/CcmiEditorAppActivity.java	Tue Feb 12 15:31:48 2013 +0000
@@ -30,6 +30,7 @@
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService;
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent;
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleDialogBuilder;
+import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
 import uk.ac.qmul.eecs.ccmi.xmlparser.Diagram;
 import android.content.Intent;
 import android.content.res.AssetManager;
@@ -40,6 +41,7 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.ArrayAdapter;
+import android.widget.TextView;
 
 /**
  * 
@@ -71,6 +73,10 @@
 		list.setOnItemClickListener(this);
 		list.setOnItemLongClickListener(this);
 		
+
+		ILogger.log("--- APPLICATION STARTED ---");
+		
+		
 		/* init assets */
 		AssetManager assetManager = getAssets();
 		try {
@@ -96,12 +102,15 @@
 	/*----- listeners to implement the navigation ----*/
 	@Override
 	public void onItemClick(AdapterView<?> av, View v, int pos, long id) {
+		ILogger.logTap(((TextView)v).getText());
 		if(!navigation.goNext(pos)){
 			accessibilityService.playSound(AccessibilityService.SoundEvent.V_ERROR);
+			ILogger.logError("end of tree");
 			return;
 		}
 		accessibilityService.playSound(AccessibilityService.SoundEvent.V_EXPAND);
-		accessibilityService.speak("Displaying " + getHeaderText()); 
+		accessibilityService.speak("displaying " + getHeaderText());
+		ILogger.logActivity(getHeaderText());
 	}
 	
 	/**
@@ -112,9 +121,12 @@
 	 */
 	@Override
 	public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+		ILogger.logLongTap(((TextView)view).getText());
 		boolean doneSomething = navigation.getController().performEditAction(parent, view, position, id, this);
-		if(!doneSomething)
+		if(!doneSomething){
 			accessibilityService.playSound(AccessibilityService.SoundEvent.V_ERROR);
+			ILogger.logError("no action available on this item");
+		}
 		return doneSomething;
 	}
 
@@ -125,9 +137,11 @@
 	 */
 	@Override
 	public void onBackPressed() {
+		ILogger.logTap("back");
 		if(navigation.goPrevious()){
 			accessibilityService.playSound(SoundEvent.V_COLLAPSE);
 			accessibilityService.speak("Displaying "+getHeaderText());
+			ILogger.logActivity(getHeaderText());
 			update();
 		}else{
 			accessibilityService.playSound(SoundEvent.V_EDITING_MODE, true);
@@ -135,19 +149,26 @@
 				@Override
 				public void onClick(View v, DialogFragment dialogFragment, String tag) {
 					if("EXIT".equals(v.getTag())){
+						ILogger.logButton("exit");
 						accessibilityService.speak(getResources().getString(R.string.exitMessage));
+						ILogger.log("--- EXIT ---");
+						ILogger.dispose();
 						finish();
 					}else if("CANCEL".equals(v.getTag())){
+						ILogger.logButton("cancel");
+						accessibilityService.playSound(SoundEvent.V_CANCEL);
 						accessibilityService.speak("Cancel");
 						accessibilityService.stopSound();
 						dialogFragment.dismiss();
 					}else if("OPEN".equals(v.getTag())){
+						ILogger.logButton("open");
 						/* start the activity to open a file */
 						Intent intent = new Intent(CcmiEditorAppActivity.this, FileSelectorActivity.Open.class);
 						intent.putExtra("extension", CCMI_EXTENSION);
 						startActivityForResult(intent,OPEN_REQUEST);
 						dialogFragment.dismiss();
 					}else if("SAVE".equals(v.getTag())){
+						ILogger.logButton("save");
 						Intent intent = new Intent(CcmiEditorAppActivity.this, FileSelectorActivity.Save.class);
 						intent.putExtra("extension", CCMI_EXTENSION);
 						startActivityForResult(intent,SAVE_REQUEST);
@@ -165,6 +186,7 @@
 				try {
 					InputStream in = new FileInputStream(new File(data.getData().getPath()));
 					readXML(in);
+					ILogger.logActivity(navigation.getCurrentPath());
 				} catch (Exception e) {
 					accessibilityService.playSound(SoundEvent.V_ERROR);
 					accessibilityService.speak("File could not be open");
--- a/src/uk/ac/qmul/eecs/ccmi/activities/TreeNavigation.java	Thu Dec 13 20:00:21 2012 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/activities/TreeNavigation.java	Tue Feb 12 15:31:48 2013 +0000
@@ -30,6 +30,7 @@
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent;
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleCheckbox;
 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleDialogBuilder;
+import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
 import uk.ac.qmul.eecs.ccmi.utilities.Stack;
 import uk.ac.qmul.eecs.ccmi.xmlparser.Diagram;
 import uk.ac.qmul.eecs.ccmi.xmlparser.DiagramUpdater;
@@ -501,7 +502,7 @@
 		};
 		
 		/*
-		 * Responds to the long cick of the user. It acts according to the list item clicked
+		 * Responds to the long click of the user. It acts according to the list item clicked
 		 * by the user (and therefore according to the hierarchy level currently displayed).
 		 * When a dialog needs to be shown, it used dialogBuilder.displayDialog() and register itself 
 		 * as buttoClickListener to handle the click of the user in the same class.   
@@ -522,19 +523,25 @@
 					/* construct the node and add it to the diagram */
 					Node node = new Node(((NodeType)clickedItem).getType(),properties);
 					diagramUpdater.addNode(node);
+					dialogBuilder.getAccessibilityService().playSound(SoundEvent.V_OK);
 					dialogBuilder.getAccessibilityService().speak("Node "+ node +" created");
+					ILogger.log("Node "+ node +" created");
 				}else{ // user clicked on edge
 					List<EdgeType> edgeTypes = diagram.getPrototypes().getEdgeTypes();
 					clickedItem = edgeTypes.get(position - nodeTypes.size());
 					/* respect min and max attached nodes */
 					if(selectedNodes.size() < ((EdgeType)clickedItem).getMinAttachedNodes()){
+						dialogBuilder.getAccessibilityService().playSound(SoundEvent.V_ERROR);
 						dialogBuilder.getAccessibilityService().speak("you must select at least "+
 								((EdgeType)clickedItem).getMinAttachedNodes()+" nodes");
+						ILogger.logError("selected nodes < "+((EdgeType)clickedItem).getMinAttachedNodes());
 						return true;
 					}
 					if(selectedNodes.size() > ((EdgeType)clickedItem).getMaxAttachedNodes()){
+						dialogBuilder.getAccessibilityService().playSound(SoundEvent.V_ERROR);
 						dialogBuilder.getAccessibilityService().speak("you must select at most "+
 								((EdgeType)clickedItem).getMaxAttachedNodes()+" nodes");
+						ILogger.logError("selected nodes > "+((EdgeType)clickedItem).getMaxAttachedNodes());
 						return true;
 					}
 					
@@ -547,6 +554,7 @@
 							edge.getAttachedNodes().add(new EdgeNode(n.getId()));
 					}
 					diagramUpdater.addEdge(edge);
+					dialogBuilder.getAccessibilityService().playSound(SoundEvent.V_OK);
 					StringBuilder builder = new StringBuilder();
 					builder.append(edge).append(" created between ");
 					for(int i=0; i<selectedNodes.size(); i++){
@@ -560,6 +568,7 @@
 					}
 					selectedNodes.clear(); // when an edge is added the selected node are cleared
 					dialogBuilder.getAccessibilityService().speak(builder.toString());
+					ILogger.log(builder.toString());
 				}
 				/* update the view */
 				cachedList = buildCurrentChildList();
@@ -611,16 +620,18 @@
 			return false;
 		}
 
-		/* this is the callback triggered when the user clicks on any bottom of the dialog shown. v is the button 
+		/* this is the callback triggered when the user clicks on any botton of the dialog shown. v is the button 
 		 * The method first checks for the dialog tag to understand which dialog it's handling, then it checks 
 		 * for the button tag to understand which button the user pressed. The first check though is on "CANCEL"
 		 * button as its tag it's the same for all the dialog */
 		@Override
 		public void onClick(View v, DialogFragment dialogFragment, String dialogTag) {
 			Object buttonTag = v.getTag();
+			ILogger.logButton(buttonTag.toString());
 			AccessibilityService accessibility = dialogBuilder.getAccessibilityService();
 			
 			if("CANCEL".equals(buttonTag)){
+				accessibility.playSound(SoundEvent.V_CANCEL);
 				accessibility.speak("Cancel");
 				dialogFragment.dismiss();
 				return;
@@ -631,10 +642,12 @@
 					selectedNodes.add((Node)clickedItem);
 					accessibility.playSound(SoundEvent.V_OK);
 					accessibility.speak(clickedItem + " selected");
+					ILogger.log(clickedItem + " selected");
 				}else if("UNSELECT".equals(buttonTag)){
 					selectedNodes.remove(clickedItem);
 					accessibility.playSound(SoundEvent.V_OK);
 					accessibility.speak(clickedItem + " unselected");
+					ILogger.log(clickedItem + " unselected");
 				}else if("RENAME".equals(buttonTag)){
 					dialogFragment.dismiss();
 					dialogBuilder.displayDialog(R.layout.alert_dialog_rename, RENAME_DIALOG_TAG+clickedItem, this);
@@ -654,19 +667,23 @@
 				if(text.length() == 0){
 					accessibility.playSound(SoundEvent.V_ERROR);
 					accessibility.speak("Text cannot be empty");
+					ILogger.logError("text empty");
 					return;
 				}
 				String oldName = clickedItem.toString();
 				diagramUpdater.rename(clickedItem,text);
 				accessibility.playSound(SoundEvent.V_OK);
 				accessibility.speak(oldName+" renamed to "+clickedItem);
+				ILogger.log(oldName+" renamed to "+clickedItem);
 			}else if(dialogTag.startsWith(CONFIRMATION_DIALOG_TAG)){
 				/* if it reaches this point it's a "YES" as a "NO" button has "CANCEL" as its tag */
 				/* and the match against "CANCEL" match is performed first of all */
 				diagramUpdater.delete(clickedItem);
 				/* if it's a selected node, remove it from selected */
 				selectedNodes.remove(clickedItem);
+				accessibility.playSound(SoundEvent.V_OK);
 				accessibility.speak(clickedItem+" Deleted");
+				ILogger.log(clickedItem+" Deleted");
 				/* update the headers which show the number of children the current item contains */
 				if(path.current() instanceof NodeType || path.current() instanceof EdgeType){
 					cachedHeaderTexts.pop();
@@ -683,11 +700,13 @@
 				if(text.length() == 0){
 					accessibility.playSound(SoundEvent.V_ERROR);
 					accessibility.speak("Text cannot be empty");
+					ILogger.logError("text empty");
 					return;
 				}
 				diagramUpdater.addProperty((Node)path.get(ITEM_LEVEL),((NodeProperty)clickedItem),text);
 				accessibility.playSound(SoundEvent.V_OK);
 				accessibility.speak("Property "+text+" added");
+				ILogger.log("Property "+text+" added");
 			}else if(EDIT_NODE_REF_DIALOG_TAG.equals(dialogTag)){
 				if("EDIT_LABEL".equals(buttonTag)){
 					dialogFragment.dismiss();
@@ -708,6 +727,7 @@
 					if(heads == null || heads.length == 0){
 						accessibility.playSound(SoundEvent.V_ERROR);
 						accessibility.speak("There are no arrow heads defined for "+edge);
+						ILogger.logError("There are no arrow heads defined for "+edge);
 						return;
 					}
 					dialogBuilder.displaySelectionDialog(EDIT_ARROWHEAD_DIALOG_TAG, heads, this);
@@ -718,16 +738,19 @@
 				if(text.length() == 0){
 					accessibility.playSound(SoundEvent.V_ERROR);
 					accessibility.speak("Text cannot be empty");
+					ILogger.logError("Text empty");
 					return;
 				}
 				diagramUpdater.setLabel((EdgeNode)clickedItem,text);
 				accessibility.playSound(SoundEvent.V_OK);
 				accessibility.speak("Label set to "+clickedItem);// EdgeNode.toString = EdgeNode.getLabel
+				ILogger.log("Label set to "+clickedItem);
 			}else if(EDIT_ARROWHEAD_DIALOG_TAG.equals(dialogTag)){
 				Spinner spinner = (Spinner)dialogFragment.getDialog().findViewById(R.id.selectionSpinner);
-				((EdgeNode)clickedItem).setHead(spinner.getSelectedItem().toString());
+				diagramUpdater.setArrowHead((EdgeNode)clickedItem, spinner.getSelectedItem().toString());
 				accessibility.playSound(SoundEvent.V_OK);
 				accessibility.speak("Arrow head set to "+spinner.getSelectedItem().toString());
+				ILogger.log("Arrow head set to "+spinner.getSelectedItem().toString());
 			}else if(EDIT_PROPERTY_DIALOG_TAG.equals(dialogTag)){
 				if("RENAME".equals(buttonTag)){
 					dialogFragment.dismiss();
@@ -766,11 +789,19 @@
 				AccessibleCheckbox checkbox = (AccessibleCheckbox)dialogFragment.getDialog().findViewById(R.id.checkBox); 
 				boolean[] checks = checkbox.getChecks();
 				List<Integer> modifiers = new ArrayList<Integer>(checks.length);
+				StringBuilder modifiersString = new StringBuilder();
 				for(int i=0; i<checks.length; i++){
-					if(checks[i])
+					if(checks[i]){
 						modifiers.add(i);
+						modifiersString.append(i).append(' ');
+					}
 				}
 				diagramUpdater.setModifiers((PropertyValue)clickedItem, modifiers);
+				accessibility.playSound(SoundEvent.V_OK);
+				accessibility.speak("modifiers set for "+path.current());
+				if(modifiersString.length() == 0)
+					modifiersString.append("no modifiers");
+				ILogger.log("modifiers set for "+path.current()+": "+ modifiersString.toString());
 			}
 			/* update the view */
 			cachedList = buildCurrentChildList();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/ccmi/utilities/ILogger.java	Tue Feb 12 15:31:48 2013 +0000
@@ -0,0 +1,98 @@
+package uk.ac.qmul.eecs.ccmi.utilities;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.logging.FileHandler;
+import java.util.logging.Formatter;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import android.os.Environment;
+
+
+public class ILogger {
+	private static final Logger LOGGER = Logger.getLogger(ILogger.class.getName());
+	private static final String LOG_PATH = "/Android/data/uk.ac.qmul.eecs.ccmi/files" ;
+	private static final String LOG_FILE = "/interaction%u.txt" ;
+	private static FileHandler fileHandler;
+	public final static int CLICK_BACK = -1;
+	
+	
+	private static void initHandler() throws IOException{
+		String state = Environment.getExternalStorageState();
+		if(!Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)){
+			throw new IOException("Could not find or write on SD Card");
+		}
+		
+		File logDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+LOG_PATH);
+		if(!logDir.exists()){
+			if(!logDir.mkdirs())
+				throw new IOException("Could not create log file directory");
+		}
+		
+		fileHandler = new FileHandler(logDir.getAbsolutePath()+LOG_FILE,true);
+		fileHandler.setFormatter(new InteractionLogFormatter());
+		LOGGER.addHandler(fileHandler);		
+	}
+
+	
+	public static void log(String message) {
+		if(fileHandler == null){
+			try {
+				initHandler();
+			} catch (IOException e) {
+				e.printStackTrace();
+				throw new RuntimeException(e);	
+			}
+		}
+		LOGGER.info(message+"\n");
+	}
+	
+	public static void logTap(CharSequence item){
+		log("user tap on: "+ item);
+	}
+	
+	public static void logLongTap(CharSequence item){
+		log("user long tap on: "+item);
+	}
+	
+	public static void logDialog(CharSequence dialog){
+		log("display dialog: "+dialog);
+	}
+	
+	public static void logButton(CharSequence button){
+		log("user button press: "+button);
+	}
+	
+	public static void logActivity(CharSequence activity){
+		log("display: "+activity);
+	}
+	
+	public static void logError(String cause ){
+		log("error: "+cause);
+	}
+	
+	public static void logHover(String text){
+		log("user hover: "+text);
+	}
+	
+	public static  void dispose(){
+		if(fileHandler != null){
+			fileHandler.close();
+			fileHandler = null;
+		}
+	}
+
+}
+
+class InteractionLogFormatter extends Formatter {
+	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS");
+	
+	@Override
+	public String format(LogRecord record) {
+		return DATE_FORMAT.format(new Date(record.getMillis()))+','+record.getMessage();
+	}
+	
+}
\ No newline at end of file