diff --git a/README.md b/README.md
index 822106811..654cc493e 100644
--- a/README.md
+++ b/README.md
@@ -29,9 +29,5 @@ Ideas
- Method for playing audio files using the system MediaPlayer, so `termux-play myfile.ogg` would play the audio file.
- Wifi network search and connect.
- real-time sensor data output instead of snap with a second delay.
-- Gyro sensor.
-- Proximity sensor.
-- Accelerometer.
-- Light sensor.
- LED Flash.
- Add extra permissions to the app to (un)install apps, stop pricesses etc.
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e7718ee64..9f82da618 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -54,6 +54,7 @@
android:exported="false"/>
+
sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
+
+ try {
+ for (int j = 0; j < sensorList.size(); ++j) {
+ Sensor sensor = sensorList.get(j);
+ sensorArray.put(sensor.getName());
+ }
+ JSONObject output = new JSONObject();
+ output.put("sensors", sensorArray);
+ result.message = output.toString(INDENTATION);
+ } catch (JSONException e) {
+ TermuxApiLogger.error("listHandler JSON error", e);
+ }
+ return result;
+ }
+ };
+
+ /**
+ * Handler for managing cleaning up sensor resources
+ */
+ static SensorCommandHandler cleanupHandler = new SensorCommandHandler() {
+ @Override
+ public SensorCommandResult handle(SensorManager sensorManager, Context context, Intent intent) {
+ SensorCommandResult result = new SensorCommandResult();
+
+ if (outputWriter != null) {
+ outputWriter.interrupt();
+ outputWriter = null;
+ sensorManager.unregisterListener(sensorEventListener);
+ result.message = "Sensor cleanup successful!";
+ TermuxApiLogger.info("Cleanup()");
+ } else {
+ result.message = "Sensor cleanup unnecessary";
+ }
+ return result;
+ }
+ };
+
+ /**
+ * Handler for managing listening to sensors
+ */
+ static SensorCommandHandler sensorHandler = new SensorCommandHandler() {
+ @Override
+ public SensorCommandResult handle(SensorManager sensorManager, Context context, Intent intent) {
+ SensorCommandResult result = new SensorCommandResult();
+ result.type = ResultType.CONTINUOUS;
+
+ clearSensorValues();
+
+ // sensor list user passed to us
+ String[] requestedSensors = getUserRequestedSensors(intent);
+ List sensorsToListenTo = getSensorsToListenTo(sensorManager, requestedSensors, intent);
+
+ if (sensorsToListenTo.isEmpty()) {
+ result.message = "No valid sensors were registered!";
+ result.type = ResultType.SINGLE;
+ } else {
+ if (outputWriter == null) {
+ outputWriter = createSensorOutputWriter(intent);
+ outputWriter.start();
+ }
+ }
+ return result;
+ }
+ };
+
+ /**
+ * Gets a string array of all user requested sensor names to listen to
+ * @param intent
+ * @return
+ */
+ protected static String[] getUserRequestedSensors(Intent intent) {
+ // sensor values passed to us from user
+ String sensorListString = intent.hasExtra("sensors") ? intent.getStringExtra("sensors") : "";
+ return sensorListString.split(",");
+ }
+
+ /**
+ * Gets a list of all sensors to listen to, that were requested and are available
+ * @param sensorManager
+ * @param requestedSensors
+ * @return
+ */
+ protected static List getSensorsToListenTo(SensorManager sensorManager, String[] requestedSensors, Intent intent) {
+ List availableSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
+ List sensorsToListenTo = new ArrayList<>();
+
+ boolean listenToAll = intent.getBooleanExtra("all", false);
+
+ if (listenToAll) {
+ for (Sensor sensor : availableSensors) {
+ sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_UI);
+ }
+ sensorsToListenTo = availableSensors;
+ TermuxApiLogger.info("Listening to ALL sensors");
+ } else {
+
+ // try to find matching sensors that were sent in request
+ for (String sensorName : requestedSensors) {
+ // ignore case
+ sensorName = sensorName.toUpperCase();
+
+ for (Sensor sensor : availableSensors) {
+ if (sensor.getName().toUpperCase().contains(sensorName)) {
+ sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_UI);
+ sensorsToListenTo.add(sensor);
+ break;
+ }
+ }
+ }
+ }
+ return sensorsToListenTo;
+ }
+
+ /**
+ * Clears out sensorEventListener as well as our sensorReadout JSON object
+ */
+ protected static void clearSensorValues() {
+ // prevent duplicate listeners
+ sensorManager.unregisterListener(sensorEventListener);
+
+ // clear out old values
+ sensorReadout = new JSONObject();
+ }
+
+
+ /**
+ * Creates SensorOutputWriter to write sensor values to stdout
+ * @param intent
+ * @return
+ */
+ protected static SensorOutputWriter createSensorOutputWriter(Intent intent) {
+ String socketAddress = intent.getStringExtra("socket_output");
+
+ outputWriter = new SensorOutputWriter(socketAddress);
+ outputWriter.setOnErrorListener(new SocketWriterErrorListener() {
+ @Override
+ public void onError(Exception e) {
+ outputWriter = null;
+ TermuxApiLogger.error("SensorOutputWriter error", e);
+ }
+ });
+ int delay = intent.getIntExtra("delay", 1000);
+ TermuxApiLogger.info("Delay set to: " + delay);
+ outputWriter.setDelay(delay);
+ return outputWriter;
+ }
+
+
+ /**
+ * Handles continuously writing Sensor info to an OutputStream asynchronously
+ */
+ static class SensorOutputWriter extends Thread {
+ // delay in milliseconds before posting new sensor reading
+ protected static final int DEFAULT_DELAY = 1000;
+
+ protected String outputSocketAddress;
+ protected boolean isRunning;
+ protected int delay;
+ protected SocketWriterErrorListener errorListener;
+
+
+ public SensorOutputWriter(String outputSocketAddress, int delay) {
+ this.outputSocketAddress = outputSocketAddress;
+ this.delay = delay;
+ }
+
+ public SensorOutputWriter(String outputSocketAddress) {
+ this(outputSocketAddress, DEFAULT_DELAY);
+ }
+
+ public boolean isRunning() {
+ return isRunning;
+ }
+
+ public void setOnErrorListener(SocketWriterErrorListener errorListener) {
+ this.errorListener = errorListener;
+ }
+
+ public void setDelay(int delay) {
+ this.delay = delay;
+ }
+
+ @Override
+ public void run() {
+ isRunning = true;
+
+ try {
+ try (LocalSocket outputSocket = new LocalSocket()) {
+ outputSocket.connect(new LocalSocketAddress(this.outputSocketAddress));
+
+ try (PrintWriter writer = new PrintWriter(outputSocket.getOutputStream())) {
+
+ while (isRunning) {
+ try {
+ Thread.sleep(this.delay);
+ } catch (InterruptedException e) {
+ TermuxApiLogger.info("SensorOutputWriter interrupted: " + e.getMessage());
+ }
+ semaphore.acquire();
+ writer.write(sensorReadout.toString(INDENTATION) + "\n");
+ writer.flush();
+ semaphore.release();
+ }
+ TermuxApiLogger.info("SensorOutputWriter finished");
+ }
+ }
+ } catch (Exception e) {
+ TermuxApiLogger.error("SensorOutputWriter error", e);
+
+ if (errorListener != null) {
+ errorListener.onError(e);
+ }
+ }
+ }
+
+ @Override
+ public void interrupt() {
+ super.interrupt();
+ this.isRunning = false;
+ }
+ }
+ }
+
+ /**
+ * Callback interface for handling exceptions that could occur in SensorOutputWriter
+ */
+ interface SocketWriterErrorListener {
+ void onError(Exception e);
+ }
+
+
+ /**
+ * Interface for handling sensor commands
+ */
+ interface SensorCommandHandler {
+ SensorCommandResult handle(SensorManager sensorManager, final Context context, final Intent intent);
+ }
+
+ /**
+ * Simple POJO to store result of executing a sensor command
+ */
+ static class SensorCommandResult {
+ public String message = "";
+ public ResultType type = ResultType.SINGLE;
+ public String error;
+ }
+
+ enum ResultType {
+ SINGLE,
+ CONTINUOUS
+ }
+
+}
+
diff --git a/app/src/main/java/com/termux/api/TermuxApiReceiver.java b/app/src/main/java/com/termux/api/TermuxApiReceiver.java
index 304a30d0c..3fcac72ee 100644
--- a/app/src/main/java/com/termux/api/TermuxApiReceiver.java
+++ b/app/src/main/java/com/termux/api/TermuxApiReceiver.java
@@ -71,6 +71,9 @@ public void onReceive(Context context, Intent intent) {
case "NotificationRemove":
NotificationAPI.onReceiveRemoveNotification(this, context, intent);
break;
+ case "Sensor":
+ SensorAPI.onReceive(context, intent);
+ break;
case "Share":
ShareAPI.onReceive(this, context, intent);
break;