Restrict app orientation on phones but not on large screen devices

Your app works great on phones in portrait orientation, so you've restricted the app to portrait only. But you see an opportunity to do more on large screens in landscape orientation.

How can you have it both ways—restrict the app to portrait orientation on small screens, but enable landscape on large?

This guide is a temporary measure until you can improve your app to provide full support for all device configurations.

Manage app orientation

To enable landscape orientation on large screens, set your app manifest to handle orientation changes by default. At runtime, determine the app window size. If the app window is small, restrict the app's orientation by overriding the manifest orientation setting.

1. Specify orientation setting in the app manifest

You can either avoid declaring the screenOrientation element of the app manifest (in which case orientation defaults to unspecified) or set screen orientation to fullUser. If the user has not locked sensor-based rotation, your app will support all device orientations.

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

The difference between unspecified and fullUser is subtle but important. If you don't declare a screenOrientation value, the system chooses the orientation, and the policy the system uses to define the orientation might differ from device to device. On the other hand, specifying fullUser matches more closely the behavior the user defined for the device: if the user has locked sensor-based rotation, the app follows the user preference; otherwise, the system allows any of the four possible screen orientations (portrait, landscape, reverse portrait, or reverse landscape). See screenOrientation.

2. Determine screen size

With the manifest set to support all user‑permitted orientations, you can specify app orientation programmatically based on screen size.

Add the Jetpack WindowManager libraries to the module's build.gradle or build.gradle.kts file:

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

Use the Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics() method to obtain the device screen size as a WindowMetrics object. The window metrics can be compared to window size classes to decide when to restrict orientation.

Windows size classes provide the breakpoints between small and large screens.

Use the WindowWidthSizeClass#COMPACT and WindowHeightSizeClass#COMPACT breakpoints to determine the screen size:

Kotlin

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass = WindowSizeClass.compute(width/density, height/density)

    return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT ||
        windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT
}

Java

/** Determines whether the device has a compact screen. **/
private boolean compactScreen() {
    WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this);
    int width = metrics.getBounds().width();
    int height = metrics.getBounds().height();
    float density = getResources().getDisplayMetrics().density;
    WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density);
    return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT ||
                windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT;
}
    Note:
  • The examples are implemented as methods of an activity; and so, the activity is dereferenced as this in the argument of computeMaximumWindowMetrics().
  • The computeMaximumWindowMetrics() method is used instead of computeCurrentWindowMetrics() because the app can be launched in multi‑window mode, which ignores the screen orientation setting. There's no point in determining the app window size and overriding the orientation setting unless the app window is the entire device screen.

See WindowManager for instructions about declaring dependencies to make the computeMaximumWindowMetrics() method available in your app.

3. Override app manifest setting

When you've determined that the device has compact screen size, you can call Activity#setRequestedOrientation() to override the manifest's screenOrientation setting:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

Java

@Override
protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstanceState);
    if (compactScreen()) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
    }
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    ViewGroup container = binding.container;

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(new View(this) {
        @Override
        protected void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            if (compactScreen()) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
            }
        }
    });
}

By adding the logic to the onCreate() and View.onConfigurationChanged() methods, you're able to obtain the maximum window metrics and override the orientation setting whenever the activity is resized or moved between displays, such as after a device rotation or when a foldable device is folded or unfolded. For more information about when configuration changes occur and when they cause activity recreation, refer to Handle configuration changes.

Key points

  • screenOrientation: App manifest setting that enables you to specify how your app responds to device orientation changes
  • Jetpack WindowManager: Set of libraries that enable you to determine the size and aspect ratio of the app window; backward compatible to API level 14
  • Activity#setRequestedOrientation(): Method with which you can change the app orientation at runtime

Results

Your app should now remain in portrait orientation on small screens regardless of device rotation. On large screens, the app should support landscape and portrait orientations.

Collections that contain this guide

This guide is part of these curated Quick Guide collections that cover broader Android development goals:

Enable your app to support an optimized user experience on tablets, foldables, and ChromeOS devices.

Have questions or feedback

Go to our frequently asked questions page and learn about quick guides or reach out and let us know your thoughts.