Android: Temporarily disable orientation changes in an Activity
Asked Answered
H

18

126

My main activity has some code that makes some database changes that should not be interrupted. I'm doing the heavy lifting in another thread, and using a progress dialog which I set as non-cancellable. However, I noticed that if I rotate my phone it restarts the activity which is REALLY bad for the process that was running, and I get a Force Close.

What I want to do is programatically disable screen orientation changes until my process completes, at which time orientation changes are enabled.

Hu answered 31/8, 2010 at 17:11 Comment(3)
Since no one seems to mention this part, you're going to want to import android.content.pm.ActivityInfo in order to use the ActivityInfo identifier.Motorbus
Look at https://mcmap.net/q/57345/-screen-orientation-lockDisrespect
Refer: https://mcmap.net/q/76971/-android-temporarily-disable-orientation-changes-in-an-activity for best solutionLizabethlizard
B
174

As explained by Chris in his self-answer, calling

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

and then

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

really works like charm... on real devices !

Don't think that it's broken when testing on the emulator, the ctrl+F11 shortcut ALWAYS change the screen orientation, without emulating sensors moves.

EDIT: this was not the best possible answer. As explained in the comments, there are issues with this method. The real answer is here.

Bos answered 1/9, 2010 at 0:2 Comment(6)
I couldn't locate those constants. Thanks for that.Hu
There's an issue with these methods... It looks like if you call setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); when the device is not in its default orientation usage, then the activity orientation is immediately changed (destroyed and recreated) to the device default orientation. For example, on a phone, if you hold it in landscape orientation, then the activity is switched to portrait and back to landscape when reactivating sensors. The same opposite issue with an Archos A5 IT : using it in portrait causes the activity to be switched to landscape and back to portrait.Bos
The real answer to the original question is there: #3821923Bos
This did not work for me. This one worked though: https://mcmap.net/q/77138/-how-to-lock-orientation-during-runtime I had to call setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); or setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); based on the current orientation retrieved from getResources().getConfiguration().orientation.Tableau
ActivityInfo.SCREEN_ORIENTATION_SENSOR does not respect Android's native orientation lock. Resetting the orientation to ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED does.Engrail
Why complicate things? What about simple adding android:screenOrientation="portrait" in the manifest for a specific activity? see: https://mcmap.net/q/53113/-how-can-i-disable-landscape-mode-in-androidNapier
S
45

None of the other answers did the trick perfectly for me, but here's what I found that does.

Lock orientation to current...

if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

When changing orientation should be allowed again, set back to default...

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
Schaerbeek answered 5/11, 2010 at 23:37 Comment(3)
The problem with this is that Configuration.ORIENTATION_PORTRAIT will be returned in both landscape modes (i.e. 'normal', and reversed). So if the phone is in reversed landscape orientation and you set it to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE it will flip upside down. In API 9, ActivityInfo introduces SCREEN_ORIENTATION_REVERSE_LANDSCAPE constant, but I don't see a way to detect such orientation through Configuration class.Nocti
This worked. The answer to the above concern is located in this answer. https://mcmap.net/q/77139/-check-orientation-on-android-phoneMcabee
Worked like a charm for my needs too, brilliant thanksMelise
A
40

Here is a more complete and up to date solution that works for API 8+, works for reverse portrait and landscape, and works on a Galaxy tab where the "natural" orientation is landscape (call activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) to unlock the orientation):

@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
public static void lockActivityOrientation(Activity activity) {
    Display display = activity.getWindowManager().getDefaultDisplay();
    int rotation = display.getRotation();
    int height;
    int width;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
        height = display.getHeight();
        width = display.getWidth();
    } else {
        Point size = new Point();
        display.getSize(size);
        height = size.y;
        width = size.x;
    }
    switch (rotation) {
    case Surface.ROTATION_90:
        if (width > height)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        break;
    case Surface.ROTATION_180:
        if (height > width)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        break;          
    case Surface.ROTATION_270:
        if (width > height)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        break;
    default :
        if (height > width)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}
Atrioventricular answered 17/5, 2013 at 0:23 Comment(3)
Worked great for me with tablet and phones.Anagram
The only correct answer which work on all kind of device for me.Monas
Definitely the best answer! You may make this method static and add Activity activity as a parameter.Squamulose
L
20

Use setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); for locking current orientation whether it be landscape or portrait.

Use setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); for unlocking orientation.

Lizabethlizard answered 1/10, 2015 at 11:0 Comment(2)
Best solution for a short temporary lock. No messing with current orientation the sensor.Peart
works on Build.VERSION.SDK_INT >= 18 , more complete answer is given by tdjprog in this page https://mcmap.net/q/76971/-android-temporarily-disable-orientation-changes-in-an-activityVagabond
T
19

In order to manage also the reverse orientation modes, I have used that code to fix the activity orientation:

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

    switch(rotation) {
    case Surface.ROTATION_180:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        break;
    case Surface.ROTATION_270:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);         
        break;
    case  Surface.ROTATION_0:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        break;
    case Surface.ROTATION_90:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        break;
    }

And to allow again the orientation:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
Tapetum answered 16/11, 2013 at 11:29 Comment(0)
H
14

I found the answer. To do this, in an Activity you can call setRequestedOrientation(int) with one of the values specified here: http://developer.android.com/reference/android/R.attr.html#screenOrientation

Before I kicked off my thread I called setRequestedOrientation(OFF) (OFF = nosensor) and when the thread was done I called setRequestedOrientation(ON) (ON = sensor). Works like a charm.

Hu answered 31/8, 2010 at 17:25 Comment(0)
F
11

Thanks all. I modified Pilot_51's solution, to make sure I restored to the previous state. I also threw in a change to support non-landscape and non-portrait screens (but haven't tested it on such a screen).

prevOrientation = getRequestedOrientation();
if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}

Then to restore it

setRequestedOrientation(prevOrientation);
Fracture answered 1/4, 2011 at 10:24 Comment(3)
Good stuff--not sure why you didn't use a switch though.Lexy
Forgot to clean up and change to a switch after I added the third option.Fracture
i found this works without having to get the current configuration if you don't have access to the activity object but only the context ActivityInfo.SCREEN_ORIENTATION_NOSENSOR|ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIEDTillion
L
9
protected void setLockScreenOrientation(boolean lock) {
    if (Build.VERSION.SDK_INT >= 18) {
        setRequestedOrientation(lock?ActivityInfo.SCREEN_ORIENTATION_LOCKED:ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        return;
    }

    if (lock) {
        switch (getWindowManager().getDefaultDisplay().getRotation()) {
            case 0: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); break; // value 1
            case 2: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); break; // value 9
            case 1: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; // value 0
            case 3: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); break; // value 8
        }
    } else
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); // value 10
}
Longeron answered 23/1, 2017 at 18:16 Comment(3)
Could you please add some explanation to your answer?Nuno
when you have some jobs in the background, just call setLockScreenOrientation(true) to lock orientation and prevent destroying the current activity in order to recreate it. when you ensure that these jobs are finished, call setLockScreenOrientation(false).Longeron
this is the best answer !Spontaneous
G
7

Here is a solution which works every time and preserves current orientation (using Activity.Info.SCREEN_ORIENTATION_PORTRAIT sets to 0° for instance, but user can have a 180° orientation as current one).

// Scope: Activity

private void _lockOrientation() {
    if (super.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
    } else {
        super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
    }
}

private void _unlockOrientation() {
    super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
Greenockite answered 28/1, 2015 at 2:30 Comment(1)
Worth to mention: API 18+ onlyVictoriavictorian
A
1

use ActivityInfo.SCREEN_ORIENTATION_USER if you want to rotate screen only if its enabled on device.

Anthropophagite answered 19/9, 2013 at 10:47 Comment(0)
A
1

This works prefect for me. It solves problem with different "natural orientation" of tablet/phone ;)

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

        Configuration config = getResources().getConfiguration();
        int naturalOrientation;

        if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) &&
                config.orientation == Configuration.ORIENTATION_LANDSCAPE)
                || ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) &&
                config.orientation == Configuration.ORIENTATION_PORTRAIT)) {
            naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
        } else {
            naturalOrientation = Configuration.ORIENTATION_PORTRAIT;
        }

        // because getRotation() gives "rotation from natural orientation" of device (different on phone and tablet)
        // we need to update rotation variable if natural orienation isn't 0 (mainly tablets)
        if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE)
            rotation = ++rotation % 4;

        switch (rotation) {
            case Surface.ROTATION_0: //0
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                break;
            case Surface.ROTATION_90: //1
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                break;
            case Surface.ROTATION_180: //2
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                break;
            case Surface.ROTATION_270: //3
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                break;
        }
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }
Antimonyl answered 30/5, 2014 at 10:9 Comment(0)
F
0

I have come up with a solution which depends on the display rotation and then decides the orientation of the device. From knowing the orientation we can lock the orientation and release it later when needed. This solution also can determine if the device in reverse landscape mode.

private void lockOrientation(){
    switch (((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation()) {


        // Portrait
        case Surface.ROTATION_0:
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            break;


        //Landscape     
        case Surface.ROTATION_90: 
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            break;


        // Reversed landscape
        case Surface.ROTATION_270:
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);             
            break;
    }
}

Then later if we need to release the orientation we can call this method:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
Fifi answered 5/10, 2014 at 22:42 Comment(0)
B
0

I think this code is easier to read.

private void keepOrientation() {

    int orientation = getResources().getConfiguration().orientation;
    int rotation = getWindowManager().getDefaultDisplay().getRotation();

    switch (rotation) {
        case Surface.ROTATION_0:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
            break;
        case Surface.ROTATION_90:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
            break;
        case Surface.ROTATION_180:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
            }
            break;
        default:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
            }
    }
}
Bathulda answered 14/10, 2014 at 14:46 Comment(0)
B
0

I have found a combination of existing rotation/orientation values are needed to cover the four possibilities; there's the portrait/landscape values and the natural orientation of the device. Let's say the devices' natural orientation will have a rotation value of 0 degrees when the screen is in it's "natural" portrait or landscape orientation. Similarly, there will be a rotation value of 90 degrees when it's in landscape or portrait (notice it's opposite of the orientation @ 0 degrees). So the rotation values that are not 0 or 90 degrees will imply "Reverse" orientation. Ok, here's some code:

public enum eScreenOrientation 
{
PORTRAIT (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT),
LANDSCAPE (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE),
PORTRAIT_REVERSE (ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT),
LANDSCAPE_REVERSE (ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE),
UNSPECIFIED_ORIENTATION (ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);

    public final int activityInfoValue;

    eScreenOrientation ( int orientation )
    {
        activityInfoValue = orientation;
    }
}



public eScreenOrientation currentScreenOrientation ( )
{
    final int rotation = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();

    final int orientation = getResources().getConfiguration().orientation;
    switch ( orientation ) 
    {
    case Configuration.ORIENTATION_PORTRAIT:
        if ( rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90 )
            return eScreenOrientation.PORTRAIT;
        else
            return eScreenOrientation.PORTRAIT_REVERSE;
    case Configuration.ORIENTATION_LANDSCAPE:
        if ( rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90 )
            return eScreenOrientation.LANDSCAPE;
        else
            return eScreenOrientation.LANDSCAPE_REVERSE;
    default:
        return eScreenOrientation.UNSPECIFIED_ORIENTATION;
    }
}

public void lockScreenOrientation ( )
    throws UnsupportedDisplayException
{
    eScreenOrientation currentOrientation = currentScreenOrientation( );
    if ( currentOrientation == eScreenOrientation.UNSPECIFIED_ORIENTATION )
        throw new UnsupportedDisplayException("Unable to lock screen - unspecified orientation");
    else
        setRequestedOrientation( currentOrientation.activityInfoValue );
}

public void unlockScreenOrientation (  )
{
    setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED );
}
Bimonthly answered 28/10, 2014 at 4:41 Comment(0)
U
0

I didn't like most of the answers here, since in the unlock they set it to UNSPECIFIED as opposed to the previous state. ProjectJourneyman did take it into account, which was great, but I preferred the locking code by Roy. So, my recommendation would be a mix of the two:

private int prevOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;

private void unlockOrientation() {
    setRequestedOrientation(prevOrientation);
}

@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private void lockOrientation() {
    prevOrientation = getRequestedOrientation();
    Display display = getWindowManager().getDefaultDisplay();
    int rotation = display.getRotation();
    int height;
    int width;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
        height = display.getHeight();
        width = display.getWidth();
    } else {
        Point size = new Point();
        display.getSize(size);
        height = size.y;
        width = size.x;
    }
    switch (rotation) {
        case Surface.ROTATION_90:
            if (width > height)
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            else
                setRequestedOrientation(9/* reversePortait */);
            break;
        case Surface.ROTATION_180:
            if (height > width)
                setRequestedOrientation(9/* reversePortait */);
            else
                setRequestedOrientation(8/* reverseLandscape */);
            break;
        case Surface.ROTATION_270:
            if (width > height)
                setRequestedOrientation(8/* reverseLandscape */);
            else
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            break;
        default :
            if (height > width)
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            else
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}
Unanimous answered 19/2, 2015 at 1:34 Comment(0)
U
0

You can use

public void swapOrientaionLockState(){
    try{
        if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION) == 1) {
            Display defaultDisplay = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.USER_ROTATION, defaultDisplay.getRotation());
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);
        } else {
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 1);
        }

        Settings.System.putInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, !orientationIsLocked() ? 1 : 0);

    } catch (Settings.SettingNotFoundException e){
        e.printStackTrace();
    }
}

public boolean orientationIsLocked(){
    if(canModifiSetting(mContext)){
        try {
            return Settings.System.getInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION) == 0;
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }
    }
    return false;
}

public static boolean canModifiSetting(Context context){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return Settings.System.canWrite(context);
    } else {
        return true;
    }
}
Unplaced answered 11/8, 2017 at 9:33 Comment(0)
Q
0

I wanted to stop rotation just on a specific screen/fragment and ended up with this code based on Ivan BASART work above,

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver

fun Fragment.setupRotationLock() {
    lifecycle.addObserver(LifecycleEventObserver { _, event ->
        val activity = activity ?: return@LifecycleEventObserver
        if (event != Lifecycle.Event.ON_PAUSE && event != Lifecycle.Event.ON_RESUME)
            return@LifecycleEventObserver
        val destination =
            if (event == Lifecycle.Event.ON_PAUSE) null
            else @Suppress("DEPRECATION") activity.windowManager?.defaultDisplay?.rotation
        activity.requestedOrientation = when (destination) {
            Surface.ROTATION_180 -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
            Surface.ROTATION_270 -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
            Surface.ROTATION_0 -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
            Surface.ROTATION_90 -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
            else -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
        }
    })
}
Quaint answered 11/4, 2023 at 10:25 Comment(0)
U
-1

use that line of code

this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  

in ur activity oncreate method

Uprush answered 30/9, 2013 at 10:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.