Search code examples
javaandroidandroid-studiosensorssensormanager

How to read Android Sensor data only once, when asked for


I'm trying to read the accelerometer and/or geomagnetic sensor in a Java android app to determine the device orientation at one point. But the sensor itself doesn't matter, and my question could be applied to any type of sensor you'd like to read, whether it'd be motion, temperature, light, pressure and what not.

I'm seeing everywhere code to setup a "stream" of data from the sensor, with event listeners and the ability to "react" to the data the sensors sends you with callbacks.

That's not what I'm after, and I think it might not be possible to do what I want.

What is want is to read the sensor data once. Not twice, not for 3 seconds straight, not when the sensor sends me an event. Turn it on, read it, turn it off.

I just need one reading, when I ask for the reading. And when I get that reading, I do not need more readings afterwards.

It feels like it should be an easy one line operation once you have sufficient permissions like anysensor.getCurrentState(). But from what I can read around, I only find documentation relying on event listeners from the sensorManager and having to setup a whole class and 5 different event callbacks :

https://developer.android.com/guide/topics/sensors/sensors_position#sensors-pos-orient

I'm guessing I can do that and try to make it as light as possible, make the eventListeners self-stop on the first reading. But it feels quite tideous and much code for what I need. And also i'm trying to do a really simple task, get the reading, to something about it, and be done with it, and this would require something asynchronous. So I guess I would have to "pause" the app until I get that first result.

Would that actually be the simplest / only way to do it ?


Solution

  • I'm writing a final answer to my question, as I found a solution that fits my specific usecase.

    Even though my action was not supposed to be "asynchronous", what I ended up doing was spliting my script so that "job.done();" carries on for the rest of my script, making the "coding" mostly similar to a synchronous script.

    For the purpose of sharing, I've striped out most of the things and tried to keep only the interesting part. And I hope it would be enough for anyone trying to do something similar. This is also the shortest way I've found to access the accelerometer's data.

    basicaly, the idear is that I have a switch, depending on the result I might need the Sensor or I might not need it (kept only two cases to illustrate my point). when the case is "portrait" I can keep everything synchronous, and call "job.done();" which will continue the rest of the script.

    However, if the case is "auto_landscape", I want to use the sensor. As I only need one reading, when the sensors sends its first callback (onSensorChanged()) it will unregister itself, and then uses the same job.done(); method to finish the script.

    Here it is :

    sensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);
    
    switch(currentMode) {
        case "portrait":
            setRotation(0);
            job.done("Portrait");
            break;
    
        case "auto_landscape":
            sensorListener = new SensorEventListener() {
                @Override public void onAccuracyChanged(Sensor arg0, int arg1) {}
                @Override public synchronized void onSensorChanged(SensorEvent event) {
                    if (event.values[0] > 0 ) {
                        // landscape right
                        setRotation(1);
                    } else {
                        // landscape left
                        setRotation(3);
                    }
                    sensorManager.unregisterListener(sensorListener);
                    job.done("Landscape (auto)");
                }
            };
            sensorManager.registerListener(sensorListener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST );
            break;
        default:
            job.done("Something is wrong!");
    }