Search code examples
androiddelayaccelerometerorientation-changessensormanager

SensorEvent onChanged Multithreading?


I'm detecting the orientation of the device to check if it's placed upside-down on a surface, then calling a method on that. The problem I ran into is the fact that a user may unintentionally turn the device's orientation to that position for a split second and return a face-up position to correct their error, however the method is still called the first time they do it.

I'm trying to add a 'double check' to the orientation on a delay of 800ms so the user has time to correct the orientation before that method is triggered. Unfortunately I cannot control how often onSensorChanged is called in order to add a thread with a 800ms delay which would double check the orientation in order to prevent the unintentional orientation change.

My code is as follows,

public SensorEventListener accelerometerListener = new SensorEventListener(){

    @Override
    public void onSensorChanged(SensorEvent event) {

            float z_value = event.values[2];

                if (z_value < 0) {
                    //the device is now upside-down

                       try {

                           Thread.sleep(800);

                           //this if statement is never called correctly because
                           // onSensorChanged is not called again.
                                if (z_value < 0) {
                                    toggleMethod();
                                }

                        } catch (InterruptedException e) {
                               e.printStackTrace();
                        }
                 }
          }

My question: Is there a way I can call onSensorChanged on a delay within the onSensorChanged method itself in order to preform a double check?


Solution

  • A very simple approach would be to give a chance for the sensor to give a new value in the upcoming n milliseconds (just as asked) would be the following:

    1- Decide on how many milliseconds you want to give the user in order to undo his action (you used 800):

    private static final long TIME_MARGIN_FOR_USER_TO_UNDO = 800;
    

    2- Create a handler and a runnable to do your action (let them be in a bigger scope than your onSensorChanged - e.g. your activity):

    Handler sensorHandler = new Handler();
    Runnable toggleRunnable = new Runnable() {
       public void run() {
          toggleMethod();
       }
    }
    

    3- Post this runnable whenever your if statement evaluates to true; However, post it after n milliseconds.

    @Override
    public void onSensorChanged(SensorEvent event) {
       float z_value = event.values[2];
       if (z_value < 0) {
          sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO);
       }
    }
    

    4- Since onSensorChanged will be called when the sensor values are changed, you can stop the runnable from running if the user fixed it. Therefore, you will need an else statement that will remove the runnable.

    @Override
    public void onSensorChanged(SensorEvent event) {
       float z_value = event.values[2];
       if (z_value < 0) {
          sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO);
       }
       else {
          sensorHandler.removeCallbacks(toggleRunnable);
       }
    }