19

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

kagronick
  • 2,552
  • 1
  • 24
  • 29
  • 3
    [This might help you](https://github.com/phuang/RotationLock/blob/master/src/org/penghuang/tools/rotationlock/RotationLockActivity.java) – Ahmad Jan 29 '13 at 16:07
  • This seems to be an end-user question, not a programming question. The answer is that you cannot globally set orientation. Any app can choose to handle orientation itself, and if it does so then it can choose to ignore changes to the device's orientation, or to change orientation based on its own criteria. Some apps do not even have the ability to display properly in landscaped (or in portrait) mode, and so overriding their chosen orientation, even if possible, would not be desirable. – Carl Feb 01 '13 at 02:49
  • 1
    You can. You make a system dialog that is hidden and set certain flags on it and request landscape orientation. I'll post the whole source code when I'm sure it works. – kagronick Feb 01 '13 at 14:29
  • @Ahmad, that seems to just disable system auto rotation, which doesn't directly give you control of what the screen orientation is. – Sam Nov 12 '14 at 20:08

4 Answers4

25

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.

Abandoned Cart
  • 4,512
  • 1
  • 34
  • 41
chrispaton
  • 361
  • 2
  • 3
  • I tried your app and found that it worked correctly. However, your code in this answer doesn't work for me. It doesn't change the screen orientation, and when I go to the home screen, the icons and text are invisible. Did you end up changing this algorithm to get it to work in your app? – Sam Jun 23 '14 at 12:29
  • 3
    On further investigation, it seems that your above code works fine when run from a service but not when run from an activity. Perhaps it's related to the Context used. (I'm not good at Android, so I don't know.) – Sam Jun 23 '14 at 13:15
  • @Sam hey thanks for the hint! calling it from a service did the job! – sjkm Sep 09 '15 at 13:39
  • @Sam Sorry I am late to the party, but it is the context that was the problem. When calling a system service from an activity, you would use `Context` in place of `Service`. I have edited the answer to reflect this information. – Abandoned Cart Oct 05 '21 at 10:05
15

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);
kagronick
  • 2,552
  • 1
  • 24
  • 29
15

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.

Sam
  • 40,644
  • 36
  • 176
  • 219
  • Where should code in point 3 be written? Also,does your snippet force orientation for all the apps or just the app in which its written? – Basher51 Dec 03 '14 at 18:12
  • @Basher51, it should preferably run before the code in point 4. The code in point 4 doesn't seem to have any effect when Android auto rotation is enabled, which is why point 3 is necessary. – Sam Dec 03 '14 at 21:04
  • @Basher51, it seems to affect all apps (which is what I meant by *globally*) except for apps that specify their own orientation preferences, so it doesn't really *force* the orientation. – Sam Dec 03 '14 at 21:05
5

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

Sam
  • 40,644
  • 36
  • 176
  • 219