9

I need to get the updated device orientation, but I have to fix my activity to Portrait (which I did in the layout xml file), which prevents me from using this:

int rotation = getWindowManager().getDefaultDisplay().getRotation();

because it always gives me the portrait rotation,

So, I'm trying to rely on the sensors. I found that Sensor.TYPE_ORIENTATION is deprecated, so I'm using a combination of Sensor.TYPE_ACCELEROMETER & Sensor.TYPE_MAGNETIC_FIELD and here is the event listener:

SensorEventListener sensorEventListener = new SensorEventListener() {
    float[] mGravity;
    float[] mGeomagnetic;

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
            mGravity = event.values;
        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
            mGeomagnetic = event.values;
        if (mGravity != null && mGeomagnetic != null) {
            float R[] = new float[9];
            float I[] = new float[9];
            boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
            if (success) {
                float orientationData[] = new float[3];
                SensorManager.getOrientation(R, orientationData);
                azimuth = orientationData[0];
                pitch = orientationData[1];
                roll = orientationData[2];
                // now how to use previous 3 values to calculate orientation
            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub

    }
};

Now Question is, how to use the 3 values azimuth, pitch & roll to detect current device orientation as one of these:

  • Landscape (rotation 0)
  • Portrait (rotation 90)
  • Reverse Landscape (rotation 180)
  • Reverse Portrait (rotation 270)
AbdelHady
  • 9,334
  • 8
  • 56
  • 83
  • There is no need for magnetic field. See http://stackoverflow.com/questions/11175599/how-to-measure-the-tilt-of-the-phone-in-xy-plane-using-accelerometer-in-android/15149421#15149421 – Hoan Nguyen Sep 23 '14 at 01:50
  • @HoanNguyen that looks good, but in this case, what would be the parameters of `getRotationMatrix` ? – AbdelHady Sep 23 '14 at 09:59
  • The device orientation is independent of azimuth, pitch and roll. If you only want to find the device orientation then there is no need to find the rotation matrix and thus no need to call getRotationMatrix. Otherwise you still need to do the calculation I posted. – Hoan Nguyen Sep 23 '14 at 20:50
  • But the answer you posted earlier is missing something "For the case of laying flat, you have to use a compass to see how much the device is rotating from the starting position." – AbdelHady Sep 24 '14 at 15:06
  • OK, you have to pass in the gravity and magnetic field for the parameters in the laying flat case and use the azimuth to see how much the device rotate from the original position. However this will not tell you the orientation of the device because the orientation is dependent on how the user held the device. It only tell you how much the device is rotate from the original position where the user held the device. – Hoan Nguyen Sep 24 '14 at 19:36
  • Creating a new object (`new float[]`) in a frequently called method is evil in a performance point of view. You should move these local arrays to members. Please, edit your gist too ;) – Louis CAD Feb 09 '16 at 16:36
  • i also confused, how to convert those values into degree? such as 0 to 360' degrees in details...? not just the 4 standards value (0, 90, 180, 270) only.... @AbdelHady – gumuruh Jan 14 '23 at 13:19

1 Answers1

18

I've found it & here is the calculating function that will be called inside the listener after reading the pitch & roll:

public static final int ORIENTATION_PORTRAIT = 0;
public static final int ORIENTATION_LANDSCAPE_REVERSE = 1;
public static final int ORIENTATION_LANDSCAPE = 2;
public static final int ORIENTATION_PORTRAIT_REVERSE = 3;
public int orientation = ORIENTATION_PORTRAIT;

private int calculateOrientation(int roll, int pitch) {
    if (((orientation == ORIENTATION_PORTRAIT || orientation == ORIENTATION_PORTRAIT_REVERSE)
            && (roll > -30 && roll < 30))) {
        if (averagePitch > 0)
            return ORIENTATION_PORTRAIT_REVERSE;
        else
            return ORIENTATION_PORTRAIT;
    } else {
        // divides between all orientations
        if (Math.abs(pitch) >= 30) {
            if (pitch > 0)
                return ORIENTATION_PORTRAIT_REVERSE;
            else
                return ORIENTATION_PORTRAIT;
        } else {
            if (averageRoll > 0) {
                return ORIENTATION_LANDSCAPE_REVERSE;
            } else {
                return ORIENTATION_LANDSCAPE;
            }
        }
    }
}

-- Update --

& here is my full utility class implementation

-- Update --

Adding this image to help visualizing the azimuth, pitch & roll: Visualizing azimuth, pitch and roll

AbdelHady
  • 9,334
  • 8
  • 56
  • 83
  • 1
    what is `averagePitch` and `averageRoll` in your code? – agamov Oct 13 '15 at 12:11
  • 2
    @agamov sorry for that :), I've updated my answer with a link for my whole implementation. – AbdelHady Oct 13 '15 at 15:53
  • Please can you shortly explain here or on the gist the logic of calculateOrientation method, thanks? – GPack Feb 26 '16 at 17:00
  • Well it was a long time ago with a lot of trial and error methodology :). But as far as I remember, when in portrait (my apps default orientation), users could `roll` the device a little right or left, but if they rolled it too much (more than 30 degrees) then we need to recalculate the new situation. See this image for a better understanding of roll and pitch http://www.mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/40876/versions/8/screenshot.jpg – AbdelHady Mar 02 '16 at 14:04