How can I globally force screen orientation in Android?

The question:

There are a number of apps that are able to force screen rotation. They work even if an app explicitly wants to be viewed in another orientation. Right now I am disabling the accelerometer rotation system setting and setting my preferred orientation. An app can still override this.

Here is one of the apps that is able to override an app’s requested orientation:

https://play.google.com/store/apps/details?id=nl.fameit.rotate&hl=en

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

I tried kagronick’s answer but couldn’t make it work. After messing around for a bit I eventually got it working using a system overlay window and removing the LayoutParams that I found were all unnecessary. Here’s my eventual solution:

orientationChanger = new LinearLayout(this);
// Using TYPE_SYSTEM_OVERLAY is crucial to make your window appear on top
// You'll need the permission android.permission.SYSTEM_ALERT_WINDOW
WindowManager.LayoutParams orientationLayout = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 0, PixelFormat.RGBA_8888);
// Use whatever constant you need for your desired rotation
orientationLayout.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;

// Optional: Replace "Context" with "Service" when used in a service
WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
wm.addView(orientationChanger, orientationLayout);
orientationChanger.setVisibility(View.VISIBLE);

You can see my success in the app I released based on this: Force Dock Rotation.

Method 2

This can be done by creating a hidden system dialog. Its kind of a hack but its crazy enough to work.

    wm = (WindowManager) content.getSystemService(Service.WINDOW_SERVICE);

    orientationChanger = new LinearLayout(content);
    orientationChanger.setClickable(false);
    orientationChanger.setFocusable(false);
    orientationChanger.setFocusableInTouchMode(false);
    orientationChanger.setLongClickable(false);

    orientationLayout = new WindowManager.LayoutParams(
            LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
            windowType, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.RGBA_8888);

    wm.addView(orientationChanger, orientationLayout);
    orientationChanger.setVisibility(View.GONE);

    orientationLayout.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    wm.updateViewLayout(orientationChanger, orientationLayout);
    orientationChanger.setVisibility(View.VISIBLE);

Method 3

As an aside, you can also globally change screen orientation without forcing it in unsupported apps. (I know this question is about overriding apps’ preferences, but this is still useful and mostly relevant info.)

  1. Grant access to system settings (WRITE_SETTINGS) in AndroidManifest.xml

    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    
  2. Import Settings

    import android.provider.Settings;
    
  3. Ensure Android screen auto-rotation is disabled

    Settings.System.putInt(
        getContentResolver(),
        Settings.System.ACCELEROMETER_ROTATION,
        0
    );
    
  4. Set USER_ROTATION to the desired setting, which should be one of the ROTATION_ constants. These values represent screen rotations from the device’s natural orientation, which could be either landscape or portrait.

    Settings.System.putInt(
        getContentResolver(),
        Settings.System.USER_ROTATION,
        Surface.ROTATION_0 //Or a different ROTATION_ constant
    );
    

Note that changes to USER_ROTATION persist even if your app is not running or is uninstalled. If I recall correctly, users can reset this value by manually disabling and enabling auto-rotation.

Method 4

Create a system view

Create an invisible View that always displays on top of other applications. Views can specify their own orientation preferences, so your view’s orientation can be used to override the orientations of underlying views and applications.

Options

I’m aware of a couple of different view types that you can use to achieve this, each with its own disadvantages.

  1. System Overlay window (TYPE_SYSTEM_OVERLAY)

    Requires the SYSTEM_ALERT_WINDOW permission. (Add the following to AndroidManifest.xml.)

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    
  2. Toast window (TYPE_TOAST)

    No permission required, except on MIUI V8.

    Doesn’t display over some window types, so their orientations mightn’t be affected:

Instructions

  1. Create a View.
  2. Create a WindowManager.LayoutParams.
    1. Set type to value you chose above.
    2. Zero its width and height to prevent problems with applications that won’t function when overlapped. (For example, Android won’t let you press the “OK” button when you try to enable an accessibility service if a window is on top of it.)
    3. Set its screenOrientation to whichever orientation value you want.

Example

Implementation

public class ScreenOrientationEnforcer {

    private final View view;
    private final WindowManager windows;

    public ScreenOrientationEnforcer(Context context) {
        windows = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        view = new View(context);
    }

    public void start() {
        WindowManager.LayoutParams layout = generateLayout();
        windows.addView(view, layout);
        view.setVisibility(View.VISIBLE);
    }

    public void stop() {
        windows.removeView(view);
    }

    private WindowManager.LayoutParams generateLayout() {
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();

        //So we don't need a permission or activity
        //Note that this won't work on some devices running MIUI
        layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;

        //Just in case the window type somehow doesn't enforce this
        layoutParams.flags =
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

        //Prevents breaking apps that detect overlying windows enabling
        //(eg UBank app, or enabling an accessibility service)
        layoutParams.width = 0;
        layoutParams.height = 0;

        //Try to make it completely invisible
        layoutParams.format = PixelFormat.TRANSPARENT;
        layoutParams.alpha = 0f;

        //The orientation to force
        layoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;

        return layoutParams;
    }

}

Usage

ScreenOrientationEnforcer screenOrientationEnforcer
    = new ScreenOrientationEnforcer(context);

//Force screen orientation
screenOrientationEnforcer.start();

//Stop forcing screen orientation
screenOrientationEnforcer.stop();

Disadvantages

  • From my experience, the view might persist if your app crashes or is killed. Restarting the device seems to work around this.

References


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment