//
//
//
//
// Ruetteln source
//
//
//
//
//
//
// File: Ruetteln/Resources/app.js
//
// Author: Francesco Prelz (Francesco.Prelz@mi.infn.it)
//
// Revision history:
// 27-Jun-2014 Initial version - base tabbed app, network communication via UDP.
// 8-Sep-2014 Modifications after debugging on iOS.
//
// Check dependencies
if (Ti.version < 1.8)
{
alert('Sorry - Ruetteln requires Titanium Mobile SDK 1.8 or later');
}
(function() {
// Render appropriate components based on the platform and form factor
var osname = Ti.Platform.osname,
version = Ti.Platform.version,
height = Ti.Platform.displayCaps.platformHeight,
width = Ti.Platform.displayCaps.platformWidth;
var destip = "";
var udport_s = 18769;
var udport_d = udport_s;
var last_acc_data_ts = 0;
var last_hdg_data_ts = 0;
var last_loc_data_ts = 0;
var max_update_hz = 5;
// considering tablets to have width over 720px and height over 600px
function checkTablet()
{
var platform = Ti.Platform.osname;
switch (platform)
{
case 'ipad':
return true;
case 'android':
var psc = Ti.Platform.Android.physicalSizeCategory;
var tiAndroid = Ti.Platform.Android;
return psc === tiAndroid.PHYSICAL_SIZE_CATEGORY_LARGE ||
psc === tiAndroid.PHYSICAL_SIZE_CATEGORY_XLARGE;
default:
return (Math.min(
Ti.Platform.displayCaps.platformHeight,
Ti.Platform.displayCaps.platformWidth
) >= 400);
}
}
var isTablet = checkTablet();
console.log(isTablet);
var tabs = Titanium.UI.createTabGroup();
tabs.addEventListener('close', function (e) {
if (Ti.Platform.name === 'android')
{
var activity = Titanium.Android.currentActivity;
if (activity) activity.finish();
}
}
);
// Application tab and its attributes
var win_a = Ti.UI.createWindow({
backgroundColor: 'white',
layout: 'vertical',
title: 'Ruetteln'
});
var tab_a = Titanium.UI.createTab({
icon:'app_tab_icon.png',
title:'Ruetteln',
window:win_a
});
label_opts = {
color:'black',
font:{fontSize:15},
text:'-',
left: 10, right:10,
width: Titanium.UI.FILL
};
var labelxy = Ti.UI.createLabel(label_opts);
win_a.add(labelxy);
var labelz = Ti.UI.createLabel(label_opts);
win_a.add(labelz);
var labellat = Ti.UI.createLabel(label_opts);
win_a.add(labellat);
var labellon = Ti.UI.createLabel(label_opts);
win_a.add(labellon);
var labeladdr = Ti.UI.createLabel(label_opts);
win_a.add(labeladdr);
var labelhsp = Ti.UI.createLabel(label_opts);
win_a.add(labelhsp);
var labelmgh = Ti.UI.createLabel(label_opts);
win_a.add(labelmgh);
var labelnet = Ti.UI.createLabel(label_opts);
labelnet.color = "green";
win_a.add(labelnet);
if (Ti.Platform.name === 'android')
{
var closebut = Ti.UI.createButton(label_opts);
closebut.title = "Close App";
closebut.addEventListener('click', function(e) {
tabs.close();
}
);
win_a.add(closebut);
}
// Graph tab and its attributes
var win_g = Ti.UI.createWindow({
backgroundColor: 'white',
layout: 'vertical',
title: 'Graph'
});
var tab_g = Titanium.UI.createTab({
icon:'graph_tab_icon.png',
title:'Graph',
window:win_g,
});
var graph_d =
{
active: 0,
dx: 0,
lvy: new Array(0,0,0),
max: 1,
min: -1,
width: width,
height: height*.7,
col: new Array('red','green','blue')
};
if (Ti.Platform.name === 'android')
{
graph_d.max = 11;
graph_d.min = -11;
};
var Canvaspkg;
var gcanv;
var graphFocusCallback = function(e)
{
graph_d.width = Ti.Platform.displayCaps.platformWidth;
graph_d.height = Ti.Platform.displayCaps.platformHeight*.66;
if (Ti.Platform.name !== 'android') gcanv.begin();
gcanv.clearRect(0,0,gcanv.rect.width,gcanv.rect.height);
if (Ti.Platform.name !== 'android') gcanv.lineWidth(2);
if (graph_d.max > 0 && graph_d.min < 0)
{
// Draw horizontal axis
var axy = graph_d.height/(graph_d.max - graph_d.min)*graph_d.max;
gcanv.beginPath();
gcanv.setColor('black');
gcanv.moveTo(0,axy);
gcanv.lineTo(graph_d.width, axy);
gcanv.stroke();
}
graph_d.active = 1;
graph_d.dx = 0;
};
var graphBlurCallback = function(e)
{
graph_d.active = 0;
};
var graphAddPoint = function(x,y,z)
{
if (graph_d.active != 0)
{
var nval = new Array(x,y,z);
var nvy = new Array(nval.length);
for (var i=0; i 0)
{
for (var i=0; i= 0 && nvy[i] <= graph_d.height &&
graph_d.lvy[i] >= 0 && graph_d.lvy[i] <= graph_d.height)
{
gcanv.setColor(graph_d.col[i]);
gcanv.beginPath();
gcanv.moveTo(graph_d.dx-1,graph_d.lvy[i]);
gcanv.lineTo(graph_d.dx, nvy[i]);
gcanv.stroke();
}
if (Ti.Platform.name !== 'android') gcanv.commit();
}
}
if (graph_d.dx < graph_d.width)
{
graph_d.dx++;
}
else
{
graphFocusCallback({});
}
for (var i=0; i= 2)
{
destip = eff_portip[0];
udport_d = parseInt(eff_portip[1]);
}
labelnet.text = 'to IP: ' + destip + ':' + udport_d
+ ' - ' + max_update_hz + 'Hz';
var settingsFocusCallback = function(e)
{
hz_input.value = max_update_hz;
};
win_s.addEventListener('focus', settingsFocusCallback);
var ipd_textfield;
var moveAddressUp = function(ipport)
{
var add_row = true;
var picker_els = portip_picker.columns[0].rows.length;
var picker_ins = picker_els - 1;
// Duplicate address in history ?
for (var i=0; i 20) // Save the last 20 addresses
{
portip_picker.columns[0].rows.shift();
if ((typeof portip_picker.reloadColumn) == "function")
portip_picker.reloadColumn(0);
}
portip_picker.setSelectedRow(0,portip_picker.columns[0].rows.length - 2,false);
var prop_portips = [];
for (var i in portip_picker.columns[0].rows)
{
prop_portips.push(
portip_picker.columns[0].rows[i].getTitle());
}
Ti.App.Properties.setList('ruettelnPortIP', prop_portips);
};
var setPortipPicker = function(portip)
{
if (portip.length <= 0) return false;
var eff_portip = portip.split(":");
var port = parseInt(eff_portip[1]);
if ((eff_portip.length < 2) || (port == null)) return false;
if ((port > 0 && port < 65536) &&
((eff_portip[0] != destip) || (port !== udport_d)))
{
destip = eff_portip[0];
udport_d = port;
moveAddressUp(destip+":"+port);
labelnet.text = 'to IP: ' + destip + ':' + udport_d + ' - '
+ max_update_hz + 'Hz';
return true;
}
return false;
};
var addNewAddressCallback = function(e)
{
var portip;
if (Ti.Platform.name === 'android')
{
portip = ipd_textfield.value;
}
else portip = e.text;
if (!setPortipPicker(portip))
{
portip_picker.setSelectedRow(0,portip_picker.columns[0].rows.length - 2,false);
}
portip_picker.addEventListener('change', settingsCallback);
};
var settingsCallback = function(e)
{
if (e.source == hz_input)
{
max_update_hz = parseInt(e.value);
labelnet.text = 'to IP: ' + destip + ':' + udport_d + ' - '
+ max_update_hz + 'Hz';
}
else if (e.source == portip_picker)
{
var title = portip_picker.getSelectedRow(0).getTitle();
if (title.search(/add new/i) >= 0)
{
// No more picker events until selection is finished
portip_picker.removeEventListener('change', settingsCallback);
// Dialog box to enter new IP:port
var dialog;
if (Ti.Platform.name === 'android')
{
ipd_textfield = Ti.UI.createTextField({value:destip + ':' + udport_d});
dialog = Ti.UI.createAlertDialog({
title: 'Enter IP address:port',
androidView: ipd_textfield,
buttonNames: ['OK', 'cancel']
});
}
else // TODO: This is not really a fallback case
{
dialog = Ti.UI.createAlertDialog({
title: 'Enter IP address:port',
style: Ti.UI.iPhone.AlertDialogStyle.PLAIN_TEXT_INPUT,
buttonNames: ['OK', 'cancel']
});
}
dialog.addEventListener('click', addNewAddressCallback);
dialog.show();
return;
}
setPortipPicker(title);
}
};
portip_picker.addEventListener('change', settingsCallback);
hz_input.addEventListener('change', settingsCallback);
var data_mode_picker = Ti.UI.createPicker({
width: '40%',
color:'orange',
backgroundColor:'blue',
left: 10, right:10
});
if (Ti.Platform.name !== 'android') data_mode_picker.height='25%';
var data_modes = [];
data_modes[0]=Ti.UI.createPickerRow({title:'JSON'});
data_modes[1]=Ti.UI.createPickerRow({title:'XML'});
data_modes[2]=Ti.UI.createPickerRow({title:'txt'});
data_mode_picker.add(data_modes);
data_mode_picker.setSelectedRow(0,0,false);
data_mode_picker.selectionIndicator = true;
if (Ti.Platform.name === 'android')
{
win_s.add(portip_label);
win_s.add(portip_picker);
}
win_s.add(hz_label);
win_s.add(hz_input);
win_s.add(data_mode_picker);
if (Ti.Platform.name !== 'android')
{
win_s.add(portip_label);
win_s.add(portip_picker);
}
// Pack tabs together.
tabs.addTab(tab_a);
tabs.addTab(tab_g);
tabs.addTab(tab_s);
tabs.open();
var UDP = require('ti.udp');
var udpsock = UDP.createSocket();
var addrtext = "-unknown-";
var udperrorCallback = function(e)
{
labelnet.color = "red";
// Pull back on update speed in case of errors.
max_update_hz = 1;
hz_input.value = max_update_hz;
};
udpsock.addEventListener('error', udperrorCallback);
udpsock.start({ port: udport_s });
var accelerometerCallback = function(e)
{
var now = new Date().getTime(); // ms since Unix epoch.
if (now < (last_acc_data_ts + (1000/max_update_hz))) return;
last_acc_data_ts = now;
labelxy.text = 'x acc: ' + e.x.toPrecision(9) +
' y acc: ' + e.y.toPrecision(9);
labelz.text = 'z acc: ' + e.z.toPrecision(9);
graphAddPoint(e.x, e.y, e.z);
var root_tag = 'accelerometer';
var send_data;
if ((data_mode_picker.getSelectedRow(0) != null) &&
(data_mode_picker.getSelectedRow(0).getTitle() == "XML"))
{
// Titanium.XML.DOMImplementation.createDocument is documented but
// missing.
// var xmldoc = Titanium.XML.DOMImplementation.createDocument("http://orsone.mi.infn.it/ruetteln", "Accelerometer data", null);
// var xmlroot = xmldoc.createElement(root_tag);
// for (var attr in e)
// {
// // TODO: Would need to check whether the attribute names are valid
// // XML tag names.
// var element = xmldoc.createElement(attr);
// element.setNodeValue(e[attr]);
// xmlroot.appendChild(element);
// }
// xmldoc.appendChild(xmlroot);
// send_data = xmldoc.GetData();
send_data = '<'+root_tag+'>';
for (var attr in e)
{
// TODO: Would need to check whether the attribute names are valid
// XML tag names.
if ((typeof e[attr]) != "object")
{
send_data += '<'+attr+'>'+e[attr]+''+attr+'>';
}
}
send_data += ''+root_tag+'>';
}
else if ((data_mode_picker.getSelectedRow(0) != null) &&
(data_mode_picker.getSelectedRow(0).getTitle() == "txt"))
{
send_data = "";
for (var attr in e)
{
if ((typeof e[attr]) != "object")
{
send_data += root_tag + '.' + attr + '==' + e[attr] + '\n';
}
}
}
else // JSON case
{
send_data = JSON.stringify(e);
}
if (destip.length > 0)
{
labelnet.color = "green";
udpsock.sendString({ host: destip, port: udport_d, data: send_data });
}
};
var geodecCallback = function(e)
{
if (e.success)
{
addrtext = e.places[0].street + ', ' +
e.places[0].city + ', ' + e.places[0].country_code;
labeladdr.text = addrtext.substr(0,120);
}
};
var locCallback = function(e)
{
var now = new Date().getTime(); // ms since Unix epoch.
if (now < (last_loc_data_ts + (1000/max_update_hz))) return;
last_loc_data_ts = now;
if (e.coords != null)
{
e.coords.address = addrtext;
labellat.text = "lat: " + e.coords.latitude;
labellon.text = "long: " + e.coords.longitude +
" +/- " + e.coords.accuracy + "m";
Titanium.Geolocation.reverseGeocoder(e.coords.latitude,
e.coords.longitude, geodecCallback);
labelhsp.text = "alt: " + e.coords.altitude
+ " +/- " + e.coords.altitudeAccuracy + " head: "
+ e.coords.heading.toPrecision(6)
+ " speed: " + e.coords.speed.toPrecision(6);
var root_tag = 'coords';
var send_data;
if ((data_mode_picker.getSelectedRow(0) != null) &&
(data_mode_picker.getSelectedRow(0).getTitle() == "XML"))
{
send_data = '<'+root_tag+'>';
for (var attr in e.coords)
{
// TODO: Would need to check whether the attribute names are valid
// XML tag names.
if ((typeof e.coords[attr]) != "object")
{
send_data += '<'+attr+'>'+e.coords[attr]+''+attr+'>';
}
}
send_data += ''+root_tag+'>';
}
else if ((data_mode_picker.getSelectedRow(0) != null) &&
(data_mode_picker.getSelectedRow(0).getTitle() == "txt"))
{
send_data = "";
for (var attr in e.coords)
{
if ((typeof e.coords[attr]) != "object")
{
send_data += root_tag + '.' + attr + '==' + e.coords[attr] + '\n';
}
}
}
else // JSON case
{
send_data = JSON.stringify(e);
}
if (destip.length > 0)
{
labelnet.color = "green";
udpsock.sendString({ host: destip, port: udport_d, data: send_data });
}
}
};
var hdgCallback = function(e)
{
var now = new Date().getTime(); // ms since Unix epoch.
if (now < (last_hdg_data_ts + (1000/max_update_hz))) return;
last_hdg_data_ts = now;
if (e.source.lastGeolocation != null)
{
if ((typeof e.source.lastGeolocation) == "string")
{
var geoobj = {};
geoobj = JSON.parse(e.source.lastGeolocation);
delete e.source.lastGeolocation;
e.source['lastGeolocation'] = geoobj;
}
if ((typeof e.source.lastGeolocation) == "object")
{
e.source.lastGeolocation.address = addrtext;
}
}
if (e.heading != null)
{
labelmgh.text = "MAG heading: "+e.heading.magneticHeading;
var root_tag = 'heading';
var send_data;
if ((data_mode_picker.getSelectedRow(0) != null) &&
(data_mode_picker.getSelectedRow(0).getTitle() == "XML"))
{
send_data = '<'+root_tag+'>';
for (var attr in e.heading)
{
// TODO: Would need to check whether the attribute names are valid
// XML tag names.
if ((typeof e.heading[attr]) != "object")
{
send_data += '<'+attr+'>'+e.heading[attr]+''+attr+'>';
}
}
if ((e.source.lastGeolocation != null) &&
((typeof e.source.lastGeolocation) == "object"))
{
send_data += '';
for (var attr in e.source.lastGeolocation)
{
// TODO: Would need to check whether the attribute names are valid
// XML tag names.
if ((typeof e.source.lastGeolocation[attr]) != "object")
{
send_data += '<'+attr+'>'+e.source.lastGeolocation[attr]+''+attr+'>';
}
}
send_data += '';
}
send_data += ''+root_tag+'>';
}
else if ((data_mode_picker.getSelectedRow(0) != null) &&
(data_mode_picker.getSelectedRow(0).getTitle() == "txt"))
{
send_data = "";
for (var attr in e.heading)
{
if ((typeof e.heading[attr]) != "object")
{
send_data += root_tag + '.' + attr + '==' + e.heading[attr] + '\n';
}
}
if ((e.source.lastGeolocation != null) &&
((typeof e.source.lastGeolocation) == "object"))
{
for (var attr in e.source.lastGeolocation)
{
if ((typeof e.source.lastGeolocation[attr]) != "object")
{
send_data += root_tag + '.lastGeolocation.' + attr + '==' + e.source.lastGeolocation[attr] + '\n';
}
}
}
}
else // JSON case
{
send_data = JSON.stringify(e);
}
if (destip.length > 0)
{
labelnet.color = "green";
udpsock.sendString({ host: destip, port: udport_d, data: send_data });
}
}
};
if (Ti.Platform.model === 'Simulator' ||
Ti.Platform.model.indexOf('sdk') !== -1)
{
alert('Warning: Accelerometer, location and compass do not work on a virtual device');
}
Ti.Accelerometer.addEventListener('update', accelerometerCallback);
if (Ti.Platform.name === 'android')
{
Ti.Android.currentActivity.addEventListener('pause', function(e) {
Ti.API.info("removing accelerometer callback on pause");
Ti.Accelerometer.removeEventListener('update', accelerometerCallback);
});
Ti.Android.currentActivity.addEventListener('resume', function(e) {
Ti.API.info("adding accelerometer callback on resume");
Ti.Accelerometer.addEventListener('update', accelerometerCallback);
});
}
Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_HIGH;
Ti.Geolocation.addEventListener("heading", hdgCallback);
Ti.Geolocation.addEventListener("location", locCallback);
var gestureCallback = function(e)
{
var splay = Ti.Media.createSound({url:"brbulb.wav"});
splay.play();
};
Ti.Gesture.addEventListener('shake', gestureCallback);
})();