/* Copyright (C) 2008-2011, Dirk Trossen, airs@dirk-trossen.de Copyright (C) 2013-2014, TecVis LP, support@tecvis.co.uk This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation as version 2.1 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.airs.handlers; import java.util.concurrent.Semaphore; import android.annotation.SuppressLint; import android.content.Context; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.Sensor; import android.os.Build; import android.os.Handler; import android.os.Message; import com.airs.R; import com.airs.platform.HandlerManager; import com.airs.platform.History; import com.airs.platform.SensorRepository; /** * Class to read internal phone sensors, specifically the Az, Pi, Ro, PR, LI, PU, TM, HU sensor * @see Handler */ @SuppressLint("NewApi") public class PhoneSensorHandler implements com.airs.handlers.Handler { private static final int INIT_LIGHT = 1; private static final int INIT_PROXIMITY = 2; private static final int INIT_ORIENTATION = 3; private static final int INIT_PRESSURE = 4; private static final int INIT_TEMPERATURE = 5; private static final int INIT_HUMIDITY = 6; private static final int INIT_PEDOMETER = 7; private Context nors; private boolean sensor_enable = false; private boolean startedOrientation = false, startedLight = false, startedProximity = false, startedPressure = false, startedTemperature = false, startedHumidity = false, startedPedometer; private SensorManager sensorManager; private android.hardware.Sensor MagField, Accelerometer, Proximity, Light, Pressure, Temperature, Humidity, Pedometer; // polltime for sensor private int polltime = 10000, polltime2 = 10000, polltime3 = 10000; // sensor values private float azimuth, roll, pitch, proximity, light, pressure, temperature, humidity; private long pedometer; private float azimuth_old, roll_old, pitch_old, proximity_old, light_old, pressure_old, temperature_old, humidity_old; private long pedometer_last = 0, pedometer_old = -1, step_counter_old = 0; private Semaphore pedometer_semaphore = new Semaphore(1); private Semaphore humidity_semaphore = new Semaphore(1); private Semaphore temperature_semaphore = new Semaphore(1); private Semaphore pressure_semaphore = new Semaphore(1); private Semaphore light_semaphore = new Semaphore(1); private Semaphore proximity_semaphore = new Semaphore(1); private Semaphore azimuth_semaphore = new Semaphore(1); private Semaphore roll_semaphore = new Semaphore(1); private Semaphore pitch_semaphore = new Semaphore(1); private boolean shutdown = false; private void wait(Semaphore sema) { try { sema.acquire(); } catch(Exception e) { } } /** * Method to acquire sensor data * For each sensor, we will fire a handler event to start the sensor reading, wait for the semaphore, then read the latest sensor value and fire another event to unregister the sensor callback -> this saves processing! * @param sensor String of the sensor symbol * @param query String of the query to be fulfilled - not used here * @see com.airs.handlers.Handler#Acquire(java.lang.String, java.lang.String) */ public byte[] Acquire(String sensor, String query) { boolean read = false; int value = 0; byte [] readings = null; // are we shutting down? if (shutdown == true) return null; // anything there? if (sensor_enable == true) { // see which sensors are requested if (sensor.equals("Az") == true) { // has Azimuth been started? if (startedOrientation == false) { // send message to handler thread to start orientation Message msg = mHandler.obtainMessage(INIT_ORIENTATION); mHandler.sendMessage(msg); } wait(azimuth_semaphore); if (azimuth != azimuth_old) { read = true; value = (int)(azimuth*10); azimuth_old = azimuth; } } if (read == false) if (sensor.equals("Pi") == true) { // has Pitch been started? if (startedOrientation == false) { // send message to handler thread to start GPS Message msg = mHandler.obtainMessage(INIT_ORIENTATION); mHandler.sendMessage(msg); } wait(pitch_semaphore); if (pitch != pitch_old) { read = true; value = (int)(pitch*10); pitch_old = pitch; } } if (read == false) if (sensor.equals("Ro") == true) { // has Roll been started? if (startedOrientation == false) { // send message to handler thread to start GPS Message msg = mHandler.obtainMessage(INIT_ORIENTATION); mHandler.sendMessage(msg); } wait(roll_semaphore); if (roll != roll_old) { read = true; value = (int)(roll*10); roll_old = roll; } } if (read == false) if (sensor.equals("PR") == true) { // has Proximity been started? if (startedProximity == false) { // send message to handler thread to start proximity Message msg = mHandler.obtainMessage(INIT_PROXIMITY); mHandler.sendMessage(msg); } wait(proximity_semaphore); if (proximity != proximity_old) { read = true; value = (int)(proximity*10); proximity_old = proximity; } } if (read == false) if (sensor.equals("LI") == true) { // has Light been started? if (startedLight == false) { // send message to handler thread to start light Message msg = mHandler.obtainMessage(INIT_LIGHT); mHandler.sendMessage(msg); } wait(light_semaphore); if (light != light_old) { read = true; value = (int)(light*10); light_old = light; } } if (read == false) if (sensor.equals("PU") == true) { // has Pressure been started? if (startedPressure == false) { // send message to handler thread to start pressure Message msg = mHandler.obtainMessage(INIT_PRESSURE); mHandler.sendMessage(msg); } wait(pressure_semaphore); if (pressure != pressure_old) { read = true; value = (int)(pressure*10); pressure_old = pressure; } } if (read == false) if (sensor.equals("HU") == true) { // has Pressure been started? if (startedHumidity == false) { // send message to handler thread to start pressure Message msg = mHandler.obtainMessage(INIT_HUMIDITY); mHandler.sendMessage(msg); } wait(humidity_semaphore); if (humidity != humidity_old) { read = true; value = (int)(humidity*10); humidity_old = humidity; } } if (read == false) if (sensor.equals("TM") == true) { // has Pressure been started? if (startedTemperature == false) { // send message to handler thread to start pressure Message msg = mHandler.obtainMessage(INIT_TEMPERATURE); mHandler.sendMessage(msg); } wait(temperature_semaphore); if (temperature != temperature_old) { read = true; value = (int)(temperature*10); temperature_old = temperature; } } if (read == false) if (sensor.equals("PD") == true) { // has Pedometer been started? if (startedPedometer == false) { // send message to handler thread to start pressure Message msg = mHandler.obtainMessage(INIT_PEDOMETER); mHandler.sendMessage(msg); } wait(pedometer_semaphore); if (pedometer != pedometer_old) { read = true; value = (int)(pedometer*10); pedometer_old = pedometer; } } } // anything to report? if (read == true) { readings = new byte[6]; readings[0] = (byte)sensor.charAt(0); readings[1] = (byte)sensor.charAt(1); readings[2] = (byte)((value>>24) & 0xff); readings[3] = (byte)((value>>16) & 0xff); readings[4] = (byte)((value>>8) & 0xff); readings[5] = (byte)(value & 0xff); } return readings; } /** * Method to share the last value of the given sensor * @param sensor String of the sensor symbol to be shared * @return human-readable string of the last sensor value * @see com.airs.handlers.Handler#Share(java.lang.String) */ public String Share(String sensor) { // see which sensors are requested if (sensor.equals("Az") == true) return "The current azimuth is " + String.valueOf(azimuth) + " degrees!"; if (sensor.equals("Pi") == true) return "The current pitch is " + String.valueOf(pitch) + " degrees!"; if (sensor.equals("Ro") == true) return "The current roll is " + String.valueOf(roll) + " degrees!"; if (sensor.equals("PR") == true) return "The current proximity is " + String.valueOf(proximity); if (sensor.equals("LI") == true) return "The current light is " + String.valueOf(light) + " lux!"; if (sensor.equals("PU") == true) return "The current pressure is " + String.valueOf(pressure) + " hPa!"; if (sensor.equals("TM") == true) return "The current temperature is " + String.valueOf(temperature) + " C!"; if (sensor.equals("HU") == true) return "The current rel. humidity is " + String.valueOf(humidity) + "%!"; if (sensor.equals("PD") == true) return "The current step count is " + String.valueOf(pedometer) + "!"; return null; } /** * Method to view historical chart of the given sensor symbol * @param sensor String of the symbol for which the history is being requested * @see com.airs.handlers.Handler#History(java.lang.String) */ public void History(String sensor) { // see which sensors are requested if (sensor.equals("Az") == true) History.timelineView(nors, "Azimuth [degrees]", sensor); if (sensor.equals("Pi") == true) History.timelineView(nors, "Pitch [degrees]", "Pi"); if (sensor.equals("Ro") == true) History.timelineView(nors, "Roll [degrees]", "Ro"); if (sensor.equals("LI") == true) History.timelineView(nors, "Light [Lux]", "LI"); if (sensor.equals("PU") == true) History.timelineView(nors, "Pressure [hPa]", "PU"); if (sensor.equals("TM") == true) History.timelineView(nors, "Temperature [C]", "TM"); if (sensor.equals("HU") == true) History.timelineView(nors, "rel. Humidity [%]", "HU"); if (sensor.equals("PD") == true) History.timelineView(nors, "step count [-]", "PD"); } /** * Method to discover the sensor symbols support by this handler * As the result of the discovery, appropriate {@link com.airs.platform.Sensor} entries will be added to the {@link com.airs.platform.SensorRepository}, if sensors can be used and depending on which sensors are available on the device * @see com.airs.handlers.Handler#Discover() * @see com.airs.platform.Sensor * @see com.airs.platform.SensorRepository */ public void Discover() { if (sensor_enable == true) { if (MagField != null && Accelerometer != null) { SensorRepository.insertSensor(new String("Az"), new String("degrees"), nors.getString(R.string.AZ_d), nors.getString(R.string.AZ_e), new String("int"), -1, 0, 3600, true, polltime, this); SensorRepository.insertSensor(new String("Pi"), new String("degrees"), nors.getString(R.string.PI_d), nors.getString(R.string.PI_e), new String("int"), -1, -1800, 1800, true, polltime, this); SensorRepository.insertSensor(new String("Ro"), new String("degrees"), nors.getString(R.string.RO_d), nors.getString(R.string.RO_e), new String("int"), -1, -900, 900, true, polltime, this); } if (Proximity != null) SensorRepository.insertSensor(new String("PR"), new String("-"), nors.getString(R.string.PR_d), nors.getString(R.string.PR_e), new String("int"), -1, 0, 1000, false, polltime2, this); if (Light != null) SensorRepository.insertSensor(new String("LI"), new String("Lux"), nors.getString(R.string.LI_d), nors.getString(R.string.LI_e), new String("int"), -1, 0, 50000, true, polltime3, this); if (Pressure != null) SensorRepository.insertSensor(new String("PU"), new String("hPa"), nors.getString(R.string.PU_d), nors.getString(R.string.PU_e), new String("int"), -1, 0, 50000, true, polltime3, this); if (Temperature != null) SensorRepository.insertSensor(new String("TM"), new String("C"), nors.getString(R.string.TM_d), nors.getString(R.string.TM_e), new String("int"), -1, 0, 50000, true, polltime3, this); if (Humidity != null) SensorRepository.insertSensor(new String("HU"), new String("%"), nors.getString(R.string.HU_d), nors.getString(R.string.HU_e), new String("int"), -1, 0, 50000, true, polltime3, this); if (Pedometer != null) SensorRepository.insertSensor(new String("PD"), new String("-"), nors.getString(R.string.PD_d), nors.getString(R.string.PD_e), new String("int"), -1, 0, 50000, true, polltime, this); } } /* * Constructor, allocating all necessary resources for the handler * Here, it's reading the preferences for the different polling intervals, checking the various sensors and arming the semaphores * @param nors Reference to the calling {@link android.content.Context} */ public PhoneSensorHandler(Context activity) { nors = activity; // read polltime polltime = HandlerManager.readRMS_i("PhoneSensorsHandler::OrientationPoll", 5) * 1000; polltime2 = HandlerManager.readRMS_i("PhoneSensorsHandler::ProximityPoll", 5) * 1000; polltime3 = HandlerManager.readRMS_i("PhoneSensorsHandler::EnvironmentalPoll", 10) * 1000; // try to open sensor services try { sensorManager = (SensorManager)nors.getSystemService(Context.SENSOR_SERVICE); MagField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); Accelerometer= sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); Proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); Light = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); Pressure = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); Temperature = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE); Humidity = sensorManager.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY); Pedometer = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); sensor_enable = true; // arm semaphores wait(pedometer_semaphore); wait(humidity_semaphore); wait(temperature_semaphore); wait(pressure_semaphore); wait(light_semaphore); wait(proximity_semaphore); wait(azimuth_semaphore); wait(roll_semaphore); wait(pitch_semaphore); } catch(Exception e) { sensor_enable = false; } } /** * Method to release all handler resources * Here, we unregister the sensor listener, if registered, and release all semaphores * @see com.airs.handlers.Handler#destroyHandler() */ public void destroyHandler() { // we are shutting down! shutdown = true; // release all semaphores for unlocking the Acquire() threads pedometer_semaphore.release(); humidity_semaphore.release(); temperature_semaphore.release(); pressure_semaphore.release(); light_semaphore.release(); proximity_semaphore.release(); azimuth_semaphore.release(); roll_semaphore.release(); pitch_semaphore.release(); // remove all messages mHandler.removeMessages(INIT_HUMIDITY); mHandler.removeMessages(INIT_PEDOMETER); mHandler.removeMessages(INIT_TEMPERATURE); mHandler.removeMessages(INIT_PRESSURE); mHandler.removeMessages(INIT_LIGHT); mHandler.removeMessages(INIT_PROXIMITY); mHandler.removeMessages(INIT_ORIENTATION); // unregister each sensor if (startedLight == true) sensorManager.unregisterListener(lightsensorlistener); if (startedProximity == true) sensorManager.unregisterListener(proximitysensorlistener); if (startedOrientation == true) sensorManager.unregisterListener(orientationsensorlistener); if (startedPressure == true) sensorManager.unregisterListener(pressuresensorlistener); if (startedTemperature == true) sensorManager.unregisterListener(temperaturesensorlistener); if (startedHumidity == true) sensorManager.unregisterListener(humiditysensorlistener); if (startedPedometer == true) sensorManager.unregisterListener(pedometersensorlistener); } // The Handler that gets information back from the other threads, initializing phone sensors // We use a handler here to allow for the Acquire() function, which runs in a different thread, to issue an initialization of the invidiaul sensors // since registerListener() can only be called from the main Looper thread!! private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (shutdown == true) return; switch (msg.what) { case INIT_PEDOMETER: if (startedPedometer == false) { // use old register for sensors before KitKat if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) startedPedometer = sensorManager.registerListener(pedometersensorlistener, Pedometer, SensorManager.SENSOR_DELAY_NORMAL); else startedPedometer = sensorManager.registerListener(pedometersensorlistener, Pedometer, SensorManager.SENSOR_DELAY_NORMAL, 0); } break; case INIT_HUMIDITY: if (startedHumidity == false) { // use old register for sensors before KitKat if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) startedHumidity = sensorManager.registerListener(humiditysensorlistener, Humidity, SensorManager.SENSOR_DELAY_NORMAL); else startedHumidity = sensorManager.registerListener(humiditysensorlistener, Humidity, SensorManager.SENSOR_DELAY_NORMAL, 0); } break; case INIT_TEMPERATURE: if (startedTemperature == false) { // use old register for sensors before KitKat if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) startedTemperature = sensorManager.registerListener(temperaturesensorlistener, Temperature, SensorManager.SENSOR_DELAY_NORMAL); else startedTemperature = sensorManager.registerListener(temperaturesensorlistener, Temperature, SensorManager.SENSOR_DELAY_NORMAL, 0); } break; case INIT_PRESSURE: if (startedPressure == false) { // use old register for sensors before KitKat if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) startedPressure = sensorManager.registerListener(pressuresensorlistener, Pressure, SensorManager.SENSOR_DELAY_NORMAL); else startedPressure = sensorManager.registerListener(pressuresensorlistener, Pressure, SensorManager.SENSOR_DELAY_NORMAL, 0); } break; case INIT_LIGHT: if (startedLight == false) { // use old register for sensors before KitKat if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) startedLight = sensorManager.registerListener(lightsensorlistener, Light, SensorManager.SENSOR_DELAY_NORMAL); else startedLight = sensorManager.registerListener(lightsensorlistener, Light, SensorManager.SENSOR_DELAY_NORMAL, 0); } break; case INIT_PROXIMITY: if (startedProximity == false) { // use old register for sensors before KitKat if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) startedProximity = sensorManager.registerListener(proximitysensorlistener, Proximity, SensorManager.SENSOR_DELAY_NORMAL); else startedProximity = sensorManager.registerListener(proximitysensorlistener, Proximity, SensorManager.SENSOR_DELAY_NORMAL, 0); } break; case INIT_ORIENTATION: if (startedOrientation == false) { // use old register for sensors before KitKat if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) { startedOrientation = sensorManager.registerListener(orientationsensorlistener, Accelerometer, SensorManager.SENSOR_DELAY_NORMAL); startedOrientation = sensorManager.registerListener(orientationsensorlistener, MagField, SensorManager.SENSOR_DELAY_NORMAL); } else { startedOrientation = sensorManager.registerListener(orientationsensorlistener, Accelerometer, SensorManager.SENSOR_DELAY_NORMAL, 0); startedOrientation = sensorManager.registerListener(orientationsensorlistener, MagField, SensorManager.SENSOR_DELAY_NORMAL, 0); } } break; default: break; } } }; private SensorEventListener orientationsensorlistener = new SensorEventListener() { private float gravity[] = null; private float geomag[] = null; public void onSensorChanged(SensorEvent event) { //gets the value switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: gravity = event.values.clone(); break; case Sensor.TYPE_MAGNETIC_FIELD: geomag = event.values.clone(); break; } getOrientation(); } private void getOrientation() { float inR[] = new float[9], I[] = new float[9]; float values[] = new float[3]; //if gravity n geomag have value, find the rotation matrix if(gravity != null && geomag != null) { //check the rotation matrix found boolean success = SensorManager.getRotationMatrix(inR, I, gravity, geomag); if(success) { // get orientation of the device SensorManager.getOrientation(inR, values); // read out values azimuth = (float)Math.toDegrees((double)values[0]); azimuth = (azimuth+360)%360; pitch = (float)Math.toDegrees((double)values[1]); roll = (float)Math.toDegrees((double)values[2]); // now release the semaphores azimuth_semaphore.release(); roll_semaphore.release(); pitch_semaphore.release(); // now unregister sensorManager.unregisterListener(orientationsensorlistener); // now flush if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) sensorManager.flush(orientationsensorlistener); startedOrientation = false; } } } public void onAccuracyChanged(android.hardware.Sensor sensor, int accuracy) { } }; private SensorEventListener proximitysensorlistener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { proximity=event.values[0]; // now release the semaphores proximity_semaphore.release(); // now unregister sensorManager.unregisterListener(proximitysensorlistener, Proximity); // now flush if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) sensorManager.flush(proximitysensorlistener); startedProximity = false; } public void onAccuracyChanged(android.hardware.Sensor sensor, int accuracy) { } }; private SensorEventListener lightsensorlistener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { light=event.values[0]; // now release the semaphores light_semaphore.release(); // now unregister sensorManager.unregisterListener(lightsensorlistener, Light); // now flush if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) sensorManager.flush(lightsensorlistener); startedLight = false; } public void onAccuracyChanged(android.hardware.Sensor sensor, int accuracy) { } }; private SensorEventListener pressuresensorlistener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { pressure=event.values[0]; // now release the semaphores pressure_semaphore.release(); // now unregister sensorManager.unregisterListener(pressuresensorlistener, Pressure); // now flush if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) sensorManager.flush(pressuresensorlistener); startedPressure = false; } public void onAccuracyChanged(android.hardware.Sensor sensor, int accuracy) { } }; private SensorEventListener temperaturesensorlistener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { temperature=event.values[0]; // now release the semaphores temperature_semaphore.release(); // now unregister sensorManager.unregisterListener(temperaturesensorlistener, Temperature); // now flush if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) sensorManager.flush(temperaturesensorlistener); startedTemperature = false; } public void onAccuracyChanged(android.hardware.Sensor sensor, int accuracy) { } }; private SensorEventListener humiditysensorlistener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { humidity=event.values[0]; // now release the semaphores humidity_semaphore.release(); // now unregister sensorManager.unregisterListener(humiditysensorlistener, Humidity); // now flush if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) sensorManager.flush(humiditysensorlistener); startedHumidity = false; } public void onAccuracyChanged(android.hardware.Sensor sensor, int accuracy) { } }; private SensorEventListener pedometersensorlistener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { // read current counter long step_counter = (long)(event.values[0]); // first reading -> then advance only one step if (pedometer_old == -1) pedometer = pedometer_last + 1; else pedometer += step_counter - step_counter_old; // store old step counter reading from sensor step_counter_old = step_counter; // now release the semaphores pedometer_semaphore.release(); } public void onAccuracyChanged(android.hardware.Sensor sensor, int accuracy) { } }; }