You first need an object that will act as a "container" for a Smarties object and will listen for Smarties events. Typically you create this object in the initialization phase of your application. It should contain (i) enough information to initialize Smarties (e.g. your wall size), and (ii) objects that will help link the container to the rest of your application. The container can be your application window.
SmartiesContainer smarties_container = new SmartiesContainer(info on your app);
Now we will describe an example SmartiesContainer class.
First import the Smarties classes (details in javadoc):
import fr.lri.smarties.libserver.Smarties; import fr.lri.smarties.libserver.SmartiesColors; import fr.lri.smarties.libserver.SmartiesEvent; import fr.lri.smarties.libserver.SmartiesPuck; import fr.lri.smarties.libserver.SmartiesDevice; import fr.lri.smarties.libserver.SmartiesWidget; import fr.lri.smarties.libserver.SmartiesWidgetHandler;
The SmartiesContainer class must be an Observer. As a Smarties object is an Observable, it can send events to the SmartiesContainer.
class SmartiesContainer implements Observer { Smarties m_smarties; // the Smarties object double appWidth, appHeight; // width & height of your wall or window ... other code of the class ... } // class SmartiesContainer
The skeleton of a constructor:
SmartiesContainer(...some objects and variables...) { ... initialize int appWidth, int appHeight ... ... initialize int tileX, int tileY that describe your ... screens tile (not very important) ... // create the Smarties object Smarties m_smaties = new Smarties( (int)appWidth, (int)appHeight, gridX, gridY) // change some defaults here if you want // the defaults are good for a tutorial ! // initalize the Smarties widgets area into a 3 x 3 grid m_smarties.initWidgets(3,3); // add some Smarties widgets in the widget area // see below // this class observes the Smarties m_smarties.addObserver(this); // run the Smarties thread m_smarties.Run(); System.out.println("Smarties running"); }
An Observer must implement an update method. This is where you receive events from the mobile interfaces (via the Smarties object):
public void update(Observable obj, Object arg) { if ( !(arg instanceof SmartiesEvent) ) { // should not happen, unless you observe other observables // with this class return; } // note that obj == m_smarties (if m_smarties is the only observed observable // of this class) SmartiesEvent e = (SmartiesEvent)arg; SmartiesPuck p = e.p; // the puck atteched to the event (could be null) SmartiesDevice dev = e.device; // the mobile device that send the event float x = e.x, y = e.y; // a relative position ([0,1]x[0,1]) w.r.t. the mobile touch area // (0,0) is top left and (1,1) is botom-right // etc see below and SmartiesEvent for more // transform mobile coordinates to your application coordinates float app_x = x*appWidth; float app_y = x*appHeight; // then we should switch w.r.t. the event type switch (e.type) { ... // handles all or some Smarties events ... default: { // events you do not care about break; } } // end switch }
We assume here that we have an AppCursor class that updates cursor movements on the wall. Here is an example code from update() that manages pucks.
// -------- SmartiesPuck Management ----------------- case SmartiesEvent.SMARTIE_EVENTS_TYPE_CREATE: { // p is a new puck! p.app_data = new AppCursor(app_x, app_y); break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_SELECT: { // the puck p has been selected (on device dev) break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_STORE: { // p has been stored AppCursor ac = (AppCursor)p.app_data; ac.hide(); break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_UNSTORE: { // p has been un-stored AppCursor ac = (AppCursor)p.app_data; ac.show(); break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_DELETE: { // p has been deleted, maybe do some stuff with p ... AppCursor ac = (AppCursor)p.app_data; ac.hide(); // and then allow the library to forget about it: m_smarties.deletePuck(p); break; }
Puck movement (simple pointing)
// -------- Gestures ------------- // note that p could be null (gesture with no selected puck) // note also that all these events are exclusive case SmartiesEvent.SMARTIE_EVENTS_TYPE_MULTI_TAPE: { // a multi tape event (e.num_tapes and e.num_fingers for last tape) break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_START_MOVE: { // p start to move (one finger!) break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_MOVE: { // p move (one finger!) if (p != null) { AppCursor ac = (AppCursor)p.app_data; ac.moveTo(app_x, app_y); } break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_END_MOVE: { // p end move (one finger) break; }
Multi-touch Smarties events:
case SmartiesEvent.SMARTIE_EVENTS_TYPE_START_MFPITCH: { // start of a multi fingers pinch: e.num_fingers, e.num_tapes before // pinch start, e.d = sum of the distances from the fingers to the barycenter break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_MFPITCH: { // multi fingers pinch: as above break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_END_MFPITCH: { // end of a multi fingers pinch (one of the fingers up) break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_START_MFMOVE: { // start of a multi fingers move: e.num_fingers, e.num_tapes before // move start, e.x and e.y position of the fingers barycenter break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_MFMOVE: { // multi fingers move, as above break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_END_MFMOVE: { // end of a multi fingers move (one of the fingers up) break; }
Keybooard events. See below to see how to map a keybaord, and note that there are also two special "text" widgets (see below again). Smarties use X Window keycode.
// -------- keyboard ------------- case SmartiesEvent.SMARTIE_EVENTS_TYPE_KEYDOWN: { // e.keycode break; } case SmartiesEvent.SMARTIE_EVENTS_TYPE_END_KEYUP: { // e.keycode break; }
Widgets callback should be explecitly called in the switch:
case SmartiesEvent.SMARTIE_EVENTS_TYPE_WIDGET: { // We must call by ourself the widgets handler. Here a piece // of generic code, the only special thing is that you can pass some // "data" Object SomeData = null; // or whatever that can help the widget handler if (e.widget.handler != null) { e.widget.handler.callback(e.widget, e, SomeData); } break; }
Pucks
The central tools in the Smarties
Widgets
In the constructor you add some widgets in the widget area of the mobile device. But first you should define the grid used to place and define the size of the the widgets:
// initalize the Smarties widgets area into a 3 x 3 grid m_smarties.initWidgets(3,3);
Then, you can start to add widget. For example, let us create a slider on the top left of the widget area with an handler that will be use when the slider is manipulated in on of the mobile devices.
sliderWidget = m_smarties.addWidget( SmartiesWidget.SMARTIES_WIDGET_TYPE_SLIDER, "Size", 1, 1, 1, 1); sliderWidget.handler = new sliderHandler();
The above code apply for all the widgets. Depending of the widget type you can or you should set other values, for instance for a slider its default value:
sliderWidget.value = 50; // in between 0 and 100
In the above code, we have defined a "handler". Such handler should be coded as follow:
public class sliderHandler implements SmartiesWidgetHandler { public boolean callback(SmartiesWidget w, SmartiesEvent e, Object user_data) { System.out.println("slider value " + w.value); if (e.p != null) { AppCursor ac = (AppCursor)e.p.app_data; ac.setSize(w.value); return true; } } }
Note that it is possible to put such an handler in an other class or in internal class (of an other class).
Now let us add a "large" button (in the middle-bottom of the widget area) that map a keyboard
w = m_smarties.addWidget( SmartiesWidget.SMARTIES_WIDGET_TYPE_BUTTON, "Keyboard", 1.5, 3, 2, 1); w.handler = new keyboardButtonClicked();
with the code for the handler:
public class keyboardButtonClicked implements SmartiesWidgetHandler { public boolean callback(SmartiesWidget w, SmartiesEvent e, Object user_data) { System.out.println("keyboardButtonClicked"); // map a keyboard in the device that ask for it m_smarties.showKeyboard(e.id, e.device); return true; } }