Screen orientation lock
Asked Answered
L

8

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.

Languorous answered 6/7, 2011 at 16:23 Comment(8)
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)Carbajal
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.Languorous
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?Gilbreath
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+Efficacy
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.Fraise
See my edit for it to work on all devices, it checks to see if the rotation has changed.Fraise
#42173364Hemialgia
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 opinionTorritorricelli
O
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;
    }
Overscore answered 9/12, 2011 at 18:56 Comment(6)
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.Tabbatha
I think you should be comparing <= FROYO instead of just < FROYO. REVERSE values aren't available until GINGERBREAD I believe.Tabbatha
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.Tabbatha
+1 for demonstrating that no one should expect locked reverse orientations <= FROYO.Divorce
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.Pivoting
Just tested it with Android 6.0 on a Nexus9. Still works like a charm.Buckish
D
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);
}
Distraught answered 6/1, 2012 at 23:38 Comment(1)
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.Tabbatha
F
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 :)

Fard answered 19/4, 2012 at 10:18 Comment(2)
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?Therapeutic
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...Fard
F
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);
    }
}
Fraise answered 24/9, 2013 at 13:14 Comment(6)
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. ThxGilbreath
@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.Fraise
@Rich, you missed the point of the inlined API... that's irrelevant to the solution. The REVERSE.. doesn't exist before 9.Fraise
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).Gilbreath
@Rich, you got it. To be fair to the other posters this full solution was added months after the original question.Fraise
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.Gimpel
F
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 ...

Flop answered 7/7, 2011 at 8:18 Comment(0)
G
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.

Gimpel answered 21/12, 2013 at 2:36 Comment(0)
R
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

Rozele answered 6/7, 2011 at 18:19 Comment(2)
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.Languorous
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..Rozele
G
-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
         }
    }
Greenhaw answered 22/9, 2011 at 9:58 Comment(1)
@NeTeInStEiN Yup. As seen in this thread as well. +1 for being one of the few who observed this.Divorce

© 2022 - 2024 — McMap. All rights reserved.