34

Is there a reliable way to lock screen orientation on all Android devices? The code below works for my Nexus S and other phones, but for some reason ROTATION_90 corresponds to SCREEN_ORIENTATION_REVERSE_PORTRAIT on the Xoom.

Is there any way to reliably map rotation to orientation?

private void lockScreenOrientation() {
    if (!mScreenOrientationLocked) {
        final int orientation = getResources().getConfiguration().orientation;
        final int rotation = getWindowManager().getDefaultDisplay().getOrientation();

        if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            }
            else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
        }
        else if (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270) {
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
            }
            else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
            }
        }

        mScreenOrientationLocked = true;
    }
}

private void unlockScreenOrientation() {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    mScreenOrientationLocked = false;
}

EDIT: This code is meant to get the current orientation and lock it. The orientation is locked temporarily, and then released to the user.

Michael Pardo
  • 2,590
  • 3
  • 24
  • 33
  • 2
    Wow, that's a lot of code just to lock orientation. I had one screen that I needed to lock orientation on, and all I needed was `setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)` – javisrk Jul 06 '11 at 16:54
  • 3
    The code locks to the current orientation, not some arbitrary orientation. The code works fine for everything except Honeycomb portrait. I could hardcode that case in there, but it's not a permanent solution, because device manufacturers can specify any rotation/orientation combination they desire. – Michael Pardo Jul 06 '11 at 19:55
  • 1
    I'm facing the exact same problem right now, and wrote almost the exact same thing as your code sample and it doesn't work on my Galaxy 10.1. Have you made any progress since posting this? – Rich Aug 02 '11 at 20:27
  • There are tons of answers on SO to this question but this is the only one I've found that works with the REVERSE orientations in gingerbread+ – tennessee sombrero Apr 04 '13 at 15:41
  • 1
    Unfortunately this only works for some devices. It seems that there are some devices where Rotation 90 is the natural "opposite" orientation, and the other half of the devices where Rotation 270 is the natural "opposite" orientation. For example this reverses the landscape view on Kindle Fire HD. – enl8enmentnow Sep 20 '13 at 14:15
  • See my edit for it to work on all devices, it checks to see if the rotation has changed. – enl8enmentnow Sep 20 '13 at 14:31
  • http://stackoverflow.com/questions/42172864/genymotion-screen-oriantation-issue-for-tablet – Aditya Vyas-Lakhan Feb 14 '17 at 05:24
  • Watch out with SCREEN_ORIENTATION_SENSOR to unlock the orientation. Read the documentation carefully: "Ignores user's setting to turn off sensor-based rotation". So is this really what you want? Unlocking can better be done with SCREEN_ORIENTATION_UNSPECIFIED in my opinion – P Kuijpers Jun 14 '17 at 12:20

8 Answers8

34

Here is my solution it works on phones and tablets in any Android SDK.

switch (getResources().getConfiguration().orientation){
        case Configuration.ORIENTATION_PORTRAIT:
            if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.FROYO){
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                if(rotation == android.view.Surface.ROTATION_90|| rotation == android.view.Surface.ROTATION_180){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                } else {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                }
            }   
        break;

        case Configuration.ORIENTATION_LANDSCAPE:
            if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.FROYO){
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            } else {
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                if(rotation == android.view.Surface.ROTATION_0 || rotation == android.view.Surface.ROTATION_90){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                } else {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                }
            }
        break;
    }
grebulon
  • 7,697
  • 5
  • 42
  • 66
Mauricio
  • 341
  • 3
  • 2
  • This works for me. I have a Xoom and a Nexus 7 to test with. The Xoom has a "natural" orientation of landscape, so it's landscape orientation values is ROTATION_0. It's portrait position is ROTATION_270. The Nexus7 has a "natural" orientation of portrait, so it's portrait orientation value is ROTATION_0, and it's landscape orientation is ROTATION_90. – SkolVikingsGuy Aug 14 '12 at 14:39
  • 1
    I think you should be comparing <= FROYO instead of just < FROYO. REVERSE values aren't available until GINGERBREAD I believe. – SkolVikingsGuy Aug 14 '12 at 14:42
  • 6
    My great fear is that these values can change per device, so that there's no way to do this properly. Android is a mess in this regard. It's amazing that there's not a simple way to get the current orientation and then later set the device to that same orientation, without spending a day figuring out how to do it and then it possibly not working correctly anyway. – SkolVikingsGuy Aug 14 '12 at 14:47
  • +1 for demonstrating that no one should expect locked *reverse* orientations <= FROYO. – ateiob Aug 21 '12 at 19:55
  • But some bad news, I've tested it on the galaxy S3 and found an unexpected behaviour. My app is set to work in landscape only. If I leave the app on then lock the phone (turn off screen), and then back into the phone, the app comes back as portrait. I debugged the code and seems that "getResources().getConfiguration().orientation" returns always portrait when coming back from a locked screen instead of the app's landscape orientation setting specified in the manifest. – PerracoLabs Mar 20 '14 at 00:37
  • Just tested it with Android 6.0 on a Nexus9. Still works like a charm. – Nantoka Oct 26 '15 at 11:11
18

I modified diyism's answers slightly to compensate for the fact that you can't use reverse_landscape and reverse_portrait modes before version 2.3

private static void disableRotation(Activity activity)
{       
    final int orientation = activity.getResources().getConfiguration().orientation;
    final int rotation = activity.getWindowManager().getDefaultDisplay().getOrientation();

    // Copied from Android docs, since we don't have these values in Froyo 2.2
    int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
    int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;

    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.FROYO)
    {
        SCREEN_ORIENTATION_REVERSE_LANDSCAPE = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        SCREEN_ORIENTATION_REVERSE_PORTRAIT = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
    }

    if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)
    {
        if (orientation == Configuration.ORIENTATION_PORTRAIT)
        {
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
        else if (orientation == Configuration.ORIENTATION_LANDSCAPE)
        {
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
    }
    else if (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270) 
    {
        if (orientation == Configuration.ORIENTATION_PORTRAIT) 
        {
            activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        }
        else if (orientation == Configuration.ORIENTATION_LANDSCAPE) 
        {
            activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        }
    }
}

private static void enableRotation(Activity activity)
{
    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
crobicha
  • 1,674
  • 1
  • 16
  • 21
  • 3
    This code doesn't work for all devices. For instance, the Xoom has ROTATION_270 mapped to PORTRAIT, not REVERSE_PORTRAIT. The solution below does work for. – SkolVikingsGuy Aug 14 '12 at 14:45
6

for a temporarily screen lock you can easily use:

//developing for android tablets **<uses-sdk android:minSdkVersion="12" />**
//works perfectly... **WATCH OUT**: look portrait to reverse-portrait on api level 13 :)

currentActivity.setRequestedOrientation(currentActivity.getResources().getConfiguration().orientation);

//to re-enable sensor, just do:

currentActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

used it for a temp screen lock during showing dialog and doing important background work..

be sure that currentActivity is valid at the time you try to access it, otherwise it wont work :)

good luck :)

cV2
  • 5,229
  • 3
  • 43
  • 53
  • It works perfectly only on some versions of android. On which ones did you test? Did you test on phones in both landscape and reverse landscape orientations? – Juozas Kontvainis Apr 23 '12 at 06:55
  • hey, it seems that if device orientation is portrait, you use your lock, it locks it to reverse portrait on api level 13 (3.2, i think).. so code should be adapted there, im on it :) sry change, i wont change it this time... – cV2 Apr 24 '12 at 07:54
3
// Works on all devices. The other solution only works on 1/2 of the devices.
// Lock orientation
int rotation = getWindowManager().getDefaultDisplay().getRotation();
lockOrientation(rotation, Surface.ROTATION_270);
// Ensure that the rotation hasn't changed
if (getWindowManager().getDefaultDisplay().getRotation() != rotation) {
    lockOrientation(rotation, Surface.ROTATION_90);
}
// ...
private void lockOrientation(int originalRotation, int naturalOppositeRotation) {
    int orientation = getResources().getConfiguration().orientation;
    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
        // Are we reverse?
        if (originalRotation == Surface.ROTATION_0 || originalRotation == naturalOppositeRotation) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        } else {
            setReversePortrait();
        }
    } else {
        // Are we reverse?
        if (originalRotation == Surface.ROTATION_0 || originalRotation == naturalOppositeRotation) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        } else {
            setReverseLandscape();
        }
    }
}

@SuppressLint("InlinedApi") 
private void setReversePortrait() {
    if (Build.VERSION.SDK_INT >= 9) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
}

@SuppressLint("InlinedApi") 
private void setReverseLandscape() {
    if (Build.VERSION.SDK_INT >= 9) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}
enl8enmentnow
  • 933
  • 7
  • 14
  • I saw your comment above about the Kindle Fire HD. I'm having a similar issue right now where I've had two different solutions (basically the same logic) that work across all my phones and tablets except for the Kindle Fire HD. Writing in a special case based on android.os.Build values feels more hacky than I'm comfortable with. Have you figured out why the Fire HD is different, an elegant solution, and/or how much of Kindle's line of devices act the same? Would love to discuss. Thx – Rich Nov 02 '13 at 04:05
  • @Rich, it's because the naturalOppositeRotation is different on some phones. Using the above code figures out what the default is for your phone by checking if the rotation has changed. I save the value of naturalOppositeRotation in user preferences once I figure it out the first time. – enl8enmentnow Nov 05 '13 at 17:38
  • @Rich, you missed the point of the inlined API... that's irrelevant to the solution. The REVERSE.. doesn't exist before 9. – enl8enmentnow Nov 09 '13 at 22:28
  • 1
    So, the genius in this is that immediately after calling setRequestedOrientation, getRotation returns the new value...I thought it would be an asynchronous process and not be immediately available. Very insightful and crazy that it's hidden way down the thread with only a single upvote (from me). – Rich Nov 10 '13 at 05:13
  • @Rich, you got it. To be fair to the other posters this full solution was added months after the original question. – enl8enmentnow Nov 17 '13 at 04:40
  • I tried this kind of technique on a Kindle Fire HD 7", and it caused the screen to flip upside down then immediately back (from the two calls to `setRequestedOrientation`). See my hard-coded solution in my own answer. You could easily use `isLandscape270()` in this solution, too, if anyone else runs into the same problem. – Eric Simonton Dec 21 '13 at 02:41
2

This solution only builds on others. It is a different way to handle the problem enl8enmentnow tackled: on some devices landscape is ROTATION_90, but on (a few) others it is ROTATION_270. When I tried something like enl8enmentnow's solution on a Kindle Fire HD 7", it made the screen rotate upside down, then immediately back. I have seen no other ideas than to hard-code which devices consider landscape to be 270, so here is that hard-coded solution:

public static void unlockOrientation() {

    activity.setRequestedOrientation(
        ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}

public static void lockOrientation() {

    if (Build.VERSION.SDK_INT < 18) {
        activity.setRequestedOrientation(getOrientation());
    } else {
        activity.setRequestedOrientation(
            ActivityInfo.SCREEN_ORIENTATION_LOCKED);
    }
}

private static int getOrientation() {

    int port = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
    int revP = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
    int land = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    int revL = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
    if (Build.VERSION.SDK_INT < 9) {
        revL = land;
        revP = port;
    } else if (isLandscape270()) {
        land = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
        revL = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    }

    Display display = activity.getWindowManager().getDefaultDisplay();
    boolean wide = activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
    switch (display.getRotation()) {
        case Surface.ROTATION_0:
            return wide ? land : port;
        case Surface.ROTATION_90:
            return wide ? land : revP;
        case Surface.ROTATION_180:
            return wide ? revL : revP;
        case Surface.ROTATION_270:
            return wide ? revL : port;
        default:
            throw new AssertionError();
    }
}

private static boolean isLandscape270() {

    return android.os.Build.MANUFACTURER.equals("Amazon")
        && !(android.os.Build.MODEL.equals("KFOT") || android.os.Build.MODEL.equals("Kindle Fire"));
}

isLandscape270() detects whether the device is a 2nd generation Kindle or later (refer to this link, getting the MODEL from this link). I do not know if other devices should also be included; please comment if you know of any.

Also, on APIs >= 18 this simply uses setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED). I have only tested that on emulators; please comment if it has problems on real devices.

Eric Simonton
  • 5,702
  • 2
  • 37
  • 54
2

Use: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR) because Orientation is determined by a physical orientation sensor: the display will rotate based on how the user moves the device. This allows any of the 4 possible rotations, regardless of what the device will normally do (for example some devices won't normally use 180 degree rotation). And your code should work on Xoom too ...

Richard
  • 423
  • 3
  • 9
1

You can declare an Activity as being only landscape or portrait in your AndroidManifest.xml. Just add the screenOrientation attribute to the activity element:

http://developer.android.com/guide/topics/manifest/activity-element.html

adam
  • 3,888
  • 2
  • 20
  • 15
  • I'm trying to lock the current orientation in code temporarily. This won't work because it's permanent. Read my code to see what I'm trying to do. – Michael Pardo Jul 07 '11 at 00:18
  • hehe, i just read the description at the top of your question which didn't really say you were looking for a programmatic reversible solution.. – adam Jul 07 '11 at 20:31
-1

My solution:

int orientation=act.getResources().getConfiguration().orientation;
int rotation=act.getWindowManager().getDefaultDisplay().getOrientation();
if (orientation==Configuration.ORIENTATION_PORTRAIT)
   {if (rotation==Surface.ROTATION_0 || rotation==Surface.ROTATION_270) //0 for phone, 270 for tablet
       {act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
       }
    else
        {act.setRequestedOrientation(9);//instead of SCREEN_ORIENTATION_REVERSE_PORTRAIT when <= android 2.2
        }
   }
else
    {if (rotation==Surface.ROTATION_90 || rotation==Surface.ROTATION_0) //90 for phone, 0 for tablet
        {act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
     else
         {act.setRequestedOrientation(8);//instead of SCREEN_ORIENTATION_REVERSE_LANDSCAPE when <= android 2.2
         }
    }
diyism
  • 12,477
  • 5
  • 46
  • 46
  • @NeTeInStEiN Yup. As seen in [this thread](http://stackoverflow.com/a/12061506/869501) as well. +1 for being one of the few who observed this. – ateiob Aug 21 '12 at 19:51