Notes on supporting Android foldables
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
- Fold in
▹ Galaxy Fold [2 Displays (Outer display, Inner foldable display)] - Fold out
▹ Huawei Mate X [1 Display]
The device opens vertically
- Fold in
▹ Galaxy Z Flip [1 Display] - Fold out
▹ None yet ?
The device opens …. !?
The unfolded display can be seamless, or have a physical screen separation
- Seamless
▹ Huawei Mate X - Screens are physically separated (ex. by a hinge)
▹ Microsoft Surface Duo
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.
- 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:
- As per Legacy Application Compatibility Mode, Android specifies a compatibility mode in which the framework operates in a ‘normal’ screen size equivalent (320dp width) mode for the benefit of legacy applications not developed for old versions of Android that pre-date screen-size independence.
- As per Android 10 Compatibility Definition — Device configuration, The smallest window size in multi-window should not be less than 220dp.
- As per Android 10 Compatibility Definition — Hardware, Android compatible handheld devices must have at least one Android-compatible display at least 2.5 inches in physical diagonal (2 inches width) size.
- In regards of a device screen size upper limit, Android 10 Compatibility Definition — Tablet Requirements refers a screen size range for tablets between 7 to 18 inches.
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.
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.
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!
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.
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:
- Microsoft’s SDK for its Surface Duo devices
▹ Surface Duo SDK — Dual-screen | Microsoft Docs - LG’s SDK for its Dual Screen adapter
▹ LG | Mobile Developer :: LG Dual Screen SDK
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
- There is a (Hinge) SensorEvent available, in which we can check how many degrees the devices has been folded, but the values provided may depend on the OEM so it is recommended to use DeviceState.getPosture() instead.
- DeviceState | Android Developers
- 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
References
- Building apps for foldables | Android Developers
- Behavior changes: apps targeting API 29+ | Android Developers
- Android Developers Blog: Get your app ready for foldable phones
- Is Your App Ready For Foldable Phones? (Android Dev Summit ’18) — YouTube
- Screens — large, small and foldable — YouTube
- Build Apps for Foldable, Multi-Display, and Large-Screen Devices (Google I/O’19) — YouTube
- Best Practices in Using the Android Emulator (Google I/O’19) — YouTube
- Expanding your app beyond the phone — YouTube
- GDC 2019: Optimizing Android games for larger screens & foldables — YouTube
- DeviceState | Android Developers
- androidx.window| Android Developers
- Microsoft releases Surface Duo preview toolkit, giving us a look into its dual-screen future
- Android 10 Compatibility Definition | Android Open Source Project
- Support New Form Factors with the new Jetpack WindowManager Library | by Kenneth Ford | Android Developers
- Introduction to dual-screen devices | Microsoft Docs
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.