/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.test.utils; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.util.Log; import org.catrobat.catroid.formulaeditor.SensorCustomEvent; import org.catrobat.catroid.formulaeditor.SensorCustomEventListener; import org.catrobat.catroid.formulaeditor.SensorManagerInterface; import org.catrobat.catroid.formulaeditor.Sensors; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class SimulatedSensorManager implements SensorManagerInterface { private static final String TAG = SimulatedSensorManager.class.getSimpleName(); public class Pair<L, R> { private L firstEntry; private R secondEntry; public Pair(L l, R r) { this.firstEntry = l; this.secondEntry = r; } public L getL() { return firstEntry; } public R getR() { return secondEntry; } } List<Pair<SensorEventListener, Sensor>> listeners; List<Pair<SensorCustomEventListener, Sensors>> customListeners; Thread simulationThread; boolean simulationThreadRunning; int timeOutInMilliSeconds = 100; // units of m/s^2 float linearAccelationMinimumX = -10; float linearAccelerationMaximumX = 10; float linearAccelationMinimumY = -10; float linearAccelerationMaximumY = 10; float linearAccelationMinimumZ = -10; float linearAccelerationMaximumZ = 10; float rotationMinimumX = (float) -Math.PI * 0.5f; float rotationMaximumX = (float) Math.PI * 0.5f; float rotationMinimumY = (float) -Math.PI * 0.5f; float rotationMaximumY = (float) Math.PI * 0.5f; float rotationMinimumZ = (float) -Math.PI * 0.5f; float rotationMaximumZ = (float) Math.PI * 0.5f; float rotationAngleMinimum = 0; float rotationAngleMaximum = (float) (2 * Math.PI); private List<SensorEvent> sentSensorEvents; private List<SensorCustomEvent> sentSensorCustomEvents; private long lastExecution = 0; public SimulatedSensorManager() { sentSensorEvents = new ArrayList<SensorEvent>(); sentSensorCustomEvents = new ArrayList<SensorCustomEvent>(); listeners = new ArrayList<Pair<SensorEventListener, Sensor>>(); customListeners = new ArrayList<Pair<SensorCustomEventListener, Sensors>>(); createSimulationThread(); } private void createSimulationThread() { simulationThread = new Thread(new Runnable() { public void run() { while (simulationThreadRunning) { lastExecution = System.currentTimeMillis(); while (System.currentTimeMillis() < lastExecution + timeOutInMilliSeconds) { Thread.yield(); } sendGeneratedSensorValues(); } } }); simulationThreadRunning = false; } public synchronized void startSimulation() { if (!simulationThreadRunning && !simulationThread.isAlive()) { simulationThreadRunning = true; simulationThread.start(); } } public synchronized void stopSimulation() { simulationThreadRunning = false; } public synchronized void unregisterListener(SensorEventListener listener) { listeners.remove(listener); Iterator<Pair<SensorEventListener, Sensor>> iterator = listeners.iterator(); while (iterator.hasNext()) { Pair<SensorEventListener, Sensor> pair = iterator.next(); if (pair.getL() == listener) { iterator.remove(); } } } public synchronized boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) { listeners.add(new Pair<SensorEventListener, Sensor>(listener, sensor)); return false; } public synchronized void unregisterListener(SensorCustomEventListener listener) { customListeners.remove(listener); Iterator<Pair<SensorCustomEventListener, Sensors>> iterator = customListeners.iterator(); while (iterator.hasNext()) { Pair<SensorCustomEventListener, Sensors> pair = iterator.next(); if (pair.getL() == listener) { iterator.remove(); } } } public synchronized boolean registerListener(SensorCustomEventListener listener, Sensors sensor) { customListeners.add(new Pair<SensorCustomEventListener, Sensors>(listener, sensor)); return false; } public synchronized void sendGeneratedSensorValues() { for (Pair<SensorEventListener, Sensor> pair : listeners) { SensorEventListener sensorEventListener = pair.getL(); Sensor sensor = pair.getR(); SensorEvent sensorEvent = null; try { Constructor<SensorEvent> constructor = SensorEvent.class.getDeclaredConstructor(int.class); constructor.setAccessible(true); sensorEvent = constructor.newInstance(3); } catch (NoSuchMethodException | IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException ex) { Log.e(TAG, "Sleep was interrupted.", ex); } if (sensorEvent == null) { return; } switch (sensor.getType()) { case Sensor.TYPE_LINEAR_ACCELERATION: sensorEvent.sensor = sensor; float[] sensorValues = new float[3]; sensorValues[0] = (float) (linearAccelationMinimumX + Math.random() * (linearAccelerationMaximumX - linearAccelationMinimumX)); sensorValues[1] = (float) (linearAccelationMinimumY + Math.random() * (linearAccelerationMaximumY - linearAccelationMinimumY)); sensorValues[2] = (float) (linearAccelationMinimumZ + Math.random() * (linearAccelerationMaximumZ - linearAccelationMinimumZ)); Reflection.setPrivateField(SensorEvent.class, sensorEvent, "values", sensorValues); sentSensorEvents.add(0, sensorEvent); sentSensorEvents = sentSensorEvents.subList(0, sentSensorEvents.size() < 50 ? sentSensorEvents.size() : 50); sensorEventListener.onSensorChanged(sensorEvent); break; case Sensor.TYPE_ROTATION_VECTOR: sensorEvent.sensor = sensor; sensorValues = new float[3]; sensorValues[0] = (float) (rotationMinimumX + Math.random() * (rotationMaximumX - rotationMinimumX)); sensorValues[1] = (float) (rotationMinimumY + Math.random() * (rotationMaximumY - rotationMinimumY)); sensorValues[2] = (float) (rotationMinimumZ + Math.random() * (rotationMaximumZ - rotationMinimumZ)); sensorValues[0] *= Math.sin((rotationAngleMinimum + Math.random() * (rotationAngleMaximum - rotationAngleMinimum)) * 0.5f); sensorValues[1] *= Math.sin((rotationAngleMinimum + Math.random() * (rotationAngleMaximum - rotationAngleMinimum)) * 0.5f); sensorValues[2] *= Math.sin((rotationAngleMinimum + Math.random() * (rotationAngleMaximum - rotationAngleMinimum)) * 0.5f); Reflection.setPrivateField(SensorEvent.class, sensorEvent, "values", sensorValues); sentSensorEvents.add(0, sensorEvent); sentSensorEvents = sentSensorEvents.subList(0, sentSensorEvents.size() < 50 ? sentSensorEvents.size() : 50); sensorEventListener.onSensorChanged(sensorEvent); break; } } for (Pair<SensorCustomEventListener, Sensors> pair : customListeners) { SensorCustomEventListener sensorEventListener = pair.getL(); Sensors sensor = pair.getR(); float[] values = new float[1]; switch (sensor) { case LOUDNESS: values[0] = (float) Math.random() * 100; break; default: break; } SensorCustomEvent sensorEvent = new SensorCustomEvent(sensor, values); sentSensorCustomEvents.add(0, sensorEvent); sentSensorCustomEvents = sentSensorCustomEvents.subList(0, sentSensorCustomEvents.size() < 50 ? sentSensorCustomEvents.size() : 50); sensorEventListener.onCustomSensorChanged(sensorEvent); } } public Sensor getDefaultSensor(int typeLinearAcceleration) { return null; } public synchronized SensorEvent getLatestSensorEvent(Sensor sensor) { Iterator<SensorEvent> iterator = sentSensorEvents.iterator(); while (iterator.hasNext()) { SensorEvent sensorEvent = iterator.next(); if (sensorEvent.sensor == sensor) { return sensorEvent; } } return null; } public synchronized SensorCustomEvent getLatestCustomSensorEvent(Sensors sensor) { Iterator<SensorCustomEvent> iterator = sentSensorCustomEvents.iterator(); while (iterator.hasNext()) { SensorCustomEvent sensorEvent = iterator.next(); if (sensorEvent.sensor == sensor) { return sensorEvent; } } return null; } }