Large screen cookbook

Android provides all the ingredients for five-star large screen apps. The recipes in this cookbook select and combine choice ingredients to solve specific development problems. Each recipe includes best practices, quality code samples, and step-by-step instructions to help you become a large screen top chef.

Star ratings

The recipes are star rated based on how well they align with the Large screen app quality guidelines.

Five-star rating Meets the criteria for Tier 1, Large screen differentiated
Four-star rating Meets the criteria for Tier 2, Large screen optimized
Three-star rating Meets the criteria for Tier 3, Large screen ready
Two-star rating Provides some large screen capabilities, but falls short of the large screen app quality guidelines
One-star rating Meets the needs of a specific use case, but doesn't properly support large screens

Chromebook camera support

Three-star rating

Get noticed on Google Play by Chromebook users.

If your camera app can function with only basic camera features, don't let app stores prevent Chromebook users from installing the app just because you inadvertently specified advanced camera features found on high-end phones.

Chromebooks have a built-in front (user-facing) camera that works well for video conferencing, snapshots, and other applications. But not all Chromebooks have a back (world-facing) camera, and most user-facing cameras on Chromebooks don't support autofocus or flash.

Best practices

Versatile camera apps support all devices regardless of camera configuration—devices with front cameras, back cameras, external cameras connected by USB.

To ensure apps stores make your app available to the greatest number of devices, always declare all camera features used by your app and explicitly indicate whether or not the features are required.

Ingredients

  • CAMERA permission — Gives your app access to a device's cameras
  • <uses-feature> manifest element — Informs app stores of the features used by your app
  • required attribute — Indicates to app stores whether your app can function without a specified feature

Steps

Summary

Declare the CAMERA permission. Declare camera features that provide basic camera support. Specify whether or not each feature is required.

1. Declare the CAMERA permission

Add the following permission to the app manifest:

<uses-permission android:name="android.permission.CAMERA" />
2. Declare basic camera features

Add the following features to the app manifest:

<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. Specify whether each feature is required

Set android:required="false" for the android.hardware.camera.any feature to enable access to your app by devices that have any kind of built-in or external camera—or no camera at all.

For the other features, set android:required="false" to ensure devices such as Chromebooks that don't have back cameras, autofocus, or flash can access your app on app stores.

Results

Chromebook users can download and install your app from Google Play and other app stores. And devices with full‑featured camera support, like phones, won't be restricted in their camera functionality.

By explicitly setting the camera features supported by your app and specifying the features your app requires, you've made your app available to as many devices as possible.

Additional resources

For more information, see Camera hardware features in the <uses-feature> documentation.

App orientation restricted on phones but not on large screen devices

Two-star rating

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?

Best practices

The best apps respect user preferences such as device orientation.

The Large screen app quality guidelines recommend that apps support all device configurations, including portrait and landscape orientations, multi-window mode, and folded and unfolded states of foldable devices. Apps should optimize layouts and user interfaces for different configurations, and apps should save and restore state during configuration changes.

This recipe is a temporary measure—a pinch of large screen support. Use the recipe until you can improve your app to provide full support for all device configurations.

Ingredients

  • screenOrientation: App manifest setting that enables you to specify how your app responds to device orientation changes
  • Jetpack WindowManager: Library that enables 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

Steps

Summary

Enable the app to handle orientation changes by default in the app manifest. 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

Set the screenOrientation element of the app manifest 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">

2. Determine screen size

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

Use the Jetpack WindowManager library's 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. The smaller dimension (width or height) of a typical phone is less than 600dp; the smaller dimension of tablets and large screen foldables, greater than 600dp.

Use the breakpoint dp values to determine the screen size:

Kotlin

/** Determines whether the device has a compact screen. **/
fun compactScreen(): Boolean {
    val screenMetrics = WindowMetricsCalculator
                        .getOrCreate()
                        .computeMaximumWindowMetrics(this)
    val shortSide = min(screenMetrics.bounds.width(),
                        screenMetrics.bounds.height())
    return shortSide / resources.displayMetrics.density < 600
}

Java

/** Determines whether the device has a compact screen. **/
public boolean compactScreen() {
    WindowMetrics screenMetrics = WindowMetricsCalculator
                                  .getOrCreate()
                                  .computeMaximumWindowMetrics(this);
    int shortSide = Math.min(screenMetrics.getBounds().width(),
                             screenMetrics.getBounds().height());
    return shortSide / getResources().getDisplayMetrics().density < 600;
}
    Note:
  • The above 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

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.

Additional resources

For help with upgrading your app to support all device configurations all the time, see the following: