Notes on supporting Android foldables

Javier
Stories by Fenrir Inc.
11 min readDec 1, 2020

Hello there! This is Francisco, Senior Android Engineer at Fenrir.
Lately, I have been gathering some information about supporting foldable devices on Android, and I would like to share some of my notes and findings.
I hope you find them helpful in your journey exploring these weird devices.

Foldable device format examples

First, a brief look on some of the many ways to fold a device.

The device opens horizontally

The device opens vertically

The device opens …. !?

Android Emulator — Foldable support sample

The unfolded display can be seamless, or have a physical screen separation

What happens when a device is folded?

Unfolding/Folding may trigger a configuration change for:

  • smallestScreenSize:
    The physical screen size has changed. This represents a change in size regardless of orientation, so will only change when the actual physical screen size has changed such as switching to an external display. A change to this configuration corresponds to a change in the `smallestWidth` configuration.
  • screenLayout:
    The screen layout has changed — a different display might now be active.
  • screenSize:
    The current available screen size has changed. This represents a change in the currently available size, relative to the current aspect ratio, so will change when the user switches between landscape and portrait.

The system will trigger a configuration change during the transition, an app should save the UI state and support configuration changes gracefully. You can expect the changes of screen size, density, or screen ratio when folding/unfolding a device to be similar to those experienced when switching between portrait and landscape mode.

Android by default recreates your activity. You can use onSaveInstanceState and ViewModel to keep your app state. Alternatively, you can handle the configuration change without restarting via android:configChanges=”…” and onConfigurationChanged()

Some considerations when developing support for foldable devices

  • Some foldable devices may have more than one physical display.
  • Foldable devices may contain screens with different aspect ratios, width and height when folded/unfolded. It is recommended to test your app behavior on foldable devices and emulators that have screens that differ from each other to verify the app appropriate transition between the different displays.
  • Be aware of cutouts in the display for cameras and other sensors. Android provides WindowInsets.getDisplayCutout() to handle them.
Android Developers — Display cutout
  • Consider using Responsive Layouts. This includes re-arranging Material Design Cards or other UI widgets contained within it.
    ▹ A few additional suggestions to optimize layouts for larger screens can be found on the Android’s App Quality Guidelines.
  • Some apps may benefit from allowing for multiple instances to be run at the same time. Using FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK allows for this, but be careful with singletons when allowing for multiple instances.
  • Further support for Foldable devices has been added from API level 29 (Android 10). Specifically, Jetpack WindowManager provides DeviceFeatures (Ex. TYPE_HINGE) and `DeviceState` (Ex. POSTURE_HALF_OPENED) values to facilitate handling possible foldable devices types, states, etc.
    ▹ These classes can be found in androidx.window:window:1.0.0-alpha01

Screens of all sizes and ratios

Foldable devices increase the possibility of the implementation of (physically) small/large and squared/slim screens. You should take into consideration the compatibility definition documents for Android Hardware provided below to have an idea of the minimum/maximum display sizes that you might find:

Android Developers — Building apps for foldables — An assortment of screen ratios

UI components that require special considerations when handling screen size changes.

  • Container views should take into consideration how to handle their contents when displayed in horizontally or vertically large screens.
  • Bottom Navigation does not stretch well. Material Design’s Navigation Rail(BETA) could be used as a replacement for horizontally large screens, but it is still in planning as an Android Material Component.
  • Horizontally oriented Steppers may make single steps look too far away when displayed in horizontally large screens.
  • Although usually fine, you should check Material Design — App Bar Bottom on horizontally large devices to make sure it works properly.
  • Lists may seem too empty when stretched. Consider the implementation of Material Design’s Lists Expand behavior, it can help display list items in a better way for different screen sizes.
Material Design — List — Expand behavior

Multi-window & Multi-resume

  • Multi-window & Multi-resume support are now essential for the app to provide a complete experience in foldable devices, the user is expected to perform multiple tasks in different apps at the same time. To provide the best experience for the user, prepare for Multi-Window and declare android:resizableActivity=”true”.
  • When an app runs on Android 10, the onResume() and onPause() methods work differently. All the focusable top activities in visible stacks are in the resumed state, but only one of them (the “topmost resumed” activity) actually has focus. Note that “topmost resumed” is NOT equal to “with focus”. The system assigns priorities to activities based on the activities that the user interacted with last. Also keep in mind that an activity can be “top-resumed”, but not have focus (for example, if the notification shade is expanded).
  • On versions earlier than Android 10, only a single activity in the system can be resumed at a time, all other visible activities are paused.
  • In Android 10 (API level 29) and later, you can subscribe to the onTopResumedActivityChanged() callback to be notified when your activity acquires or loses the topmost resumed position (this is the “new” onResume())
  • Multi-Resume depends on Android OS >= P, but it also depends on OEM opt-in.
  • Multi resume is expected to become mandatory in a P+ versions of Android (as presented on 2018’s Android Dev Summit).
  • To Opt-in your app for Multi-resume, add the meta-data tag android.allow_multiple_resumed_activities. Many activities from the same app can be resumed so you must be careful with your singletons and those of Frameworks that you may use!
  • Implementation of Drag and Drop support is recommended.

Free-Form Windows are already supported by some foldable devices (Samsung Galaxy devices, under the name of “Pop-up Window”), and this Android OS native implementation feature is accessible for testing in Android Q. It is a good idea to keep in mind that the user might want to use the app this way to allow for multitasking.

Many windows, many sizes

Camera

  • Consider that multiple apps may want to use the camera, and all of them are on the OnResume() state when visible. You must watch out for camera callbacks and connect/disconnect accordingly from the camera.
  • Note that resizableActivity = false does not guarantee camera access!
Android Developers — Multiple apps trying to access the camera.

Activities on secondary displays

  • There is a new intent filter category SECONDARY_HOME to provide a dedicated activity for secondary screens. Instances of this activity are used on all displays that support system decorations, one per each display.
    ▹ When an app is moved between displays:
    1) You should support window resize.
    2) A Context update takes place.
    3) You should prepare for resources references changes.
    (This is a result of the Context update)
  • The Application Context and Activity Context are different. The Activity Context contains the information of the display in which it is being shown. Display metrics and resources should be taken from the Activity Context.

Providing minimum support for foldables

  • Project targetSdkVersion should target at least API level 29.
  • Check for the appropriate use of Context as the switch between screens might trigger a context update and some resources references can get outdated.

If your app UI is not suitable for very wide aspect ratios

You can consider letter-boxing (although, an adaptable UI works best!). Be advised that a small screen might display the requested minimum aspect ratio, but the actual space in the screen might not be enough to display the contents of the app.

  • Set Activities as not auto-resizable
  • Set a specific screenOrientation
  • Define the min/max aspect ratios supported by the app, this triggers the Android OS to display it with letter-boxing.
  • Add the meta-data android.supports_size_changes as true to your activities.
  • Add the following configChanges attributes to your activities:
android:configChanges="screenLayout|smallestScreenSize|screenSize|orientation"
  • The user can recreate the app once the display has changed to make it fit the screen if the android.supports_size_changes meta-data tag is not added, but setting min/max ratios can help make it fill the screen in a ratio that does not break the app. Also, the aspect ratio will stay the same as when the app was launched. As a result, moving from an unfolded screen to small folded screen will make it look letter boxed on the small screen until the “resize” button is clicked. This button is OEM dependent.
Android Developers — Original size is kept for the app using letter-boxing. The “resize” button can be found on the bottom right corner.

If your app UI can adapt to wide aspect ratios

  • Set Activities as not auto-resizable
  • Add the meta-data android.supports_size_changes as true to your activities.
  • Add the following configChanges attributes to your activities:
android:configChanges="screenLayout|smallestScreenSize|screenSize|orientation"
  • App behavior on devices that have a hinge that physically separates the screens is suggested to be ignored due to the complexity involved on adapting the screens and views to the separated display areas to prevent content from being hidden behind the screens separation.

Dummy Manifest File

This is a dummy manifest file where you can check where the different meta-data tags should be added:

<?xml version=”1.0" encoding=”utf-8"?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android"
package=”com.testing.foldableapp”>
<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/Theme.FoldableApp”>
<meta-data
android:name=”android.max_aspect”
android:value=”2.4" />
<meta-data
android:name=”android.min_aspect”
android:value=”1.8" />
<activity
android:name=”.MainActivity”
android:maxAspectRatio=”2.4"
android:minAspectRatio=”1.8"
android:configChanges=
"screenLayout|smallestScreenSize|screenSize|orientation"
android:resizeableActivity=”false”>
<meta-data
android:name=”android.supports_size_changes”
android:value=”true” />
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>
</manifest>

Providing Full support for Foldables

Standard approach for all devices

  • Project targetSdkVersion should be at least API level 29 to take advantage of the OS support for foldables.
  • Below features should be supported. As a side effect, the app will also be ready for Android Desktop mode and Chrome OS devices.
    ▹ Implement Configuration changes
    ▹ Implement Multi-Window Support
    ▹ Implement Multi-Resume
    ▹ Implement Multi-Display
    ▹ (Optional) Implement Drag and Drop
  • App behavior on devices that have a hinge that physically separates the screens is suggested to be ignored due to the complexity involved on adapting the screens and views to the separated display areas to prevent content from being hidden behind the screens separation.

Supporting Devices with a physical separation between the screens

Microsoft has compiled an interesting introduction to dual-screen foldable devices, their focus on the support of devices that have a physical separation between the screens provide good insights on many UI elements that need to be adapted, as well as many ways to arrange content.

Android OS Tools

Android OS provides DisplayFeature.getBounds() to detect the Rect that describes the bounds covered by the separation between screens (the mask):

Get bounding rectangle of the physical display feature in the coordinate space of the window. The rectangle provides information about the location of the feature in the window and its size.

After obtaining the DisplayFeature Rect, it is necessary to validate if the location of said Rect obstructs any of the views displayed by the app. If it does cover any view, the app should resize and relocate its views to display its content outside of the area of the display covered by the Rect. Do keep in mind that this Rect might be vertical or horizontal (device folds horizontally or vertically), and might vary in width/height depending on the physical structure of the device.

Specific device SDKs support

OEMs may provide specialized SDKs targeted at their devices. For example:

For practical purposes, you should consider using these SDKs only if you specifically plan to support such device. SDKs related to foldables are still maturing, hence it might be a good idea to hold for a little while before committing to vendor specific code.

Helpful values, classes and tools

Android Developers — Postures
  • DeviceFeatures | Android Developers
    Sample Features:
    TYPE_HINGE
    TYPE_FOLD
  • The android emulator supports folding the emulated device by degrees or posture, but it is still a work in progress feature at the time of writing. To try it out you need to add below values to your foldable emulator config.ini as provided by Google:
hw.sensor.hinge = yes
hw.sensor.hinge.count = 1
hw.sensor.hinge.type = 1
hw.sensor.hinge.ranges = 180–360
hw.sensor.hinge.defaults = 180
hw.sensor.hinge.areas = 54.7–0
hw.sensor.posture_list=4, 3
hw.sensor.hinge_angles_posture_definitions=210–360, 180–210
hw.sensor.hinge.fold_to_displayRegion.0.1_at_posture=4

Alternatively, you can use below values to simulate an 「Horizontal & Fold-in」 device that provides all 4 device states:

hw.sensor.hinge = yes
hw.sensor.hinge.count = 1
hw.sensor.hinge.type = 1
hw.sensor.hinge.ranges = 0–360
hw.sensor.hinge.defaults = 180
hw.sensor.hinge.areas = 54.7–0
hw.sensor.posture_list=4, 3, 2, 1
hw.sensor.hinge_angles_posture_definitions=210–360, 150–210, 30–150, 0–30
Foldable Android emulator in action

Portions of this page are reproduced from work created and shared by the Android Open Source Project and are used in accordance to the terms described in the Creative Commons 2.5 Attribution License.

The Android robot is reproduced or modified from work created and shared by Google and used in accordance to the terms described in the Creative Commons 3.0 Attribution License.

--

--