Exploring Jetpack WindowManager for Foldable Devices

...

Over the last decade, the evolution of mobile devices has rapidly expanded the ecosystem of device form factors. Foldable devices are one of the more recent additions that has pushed the boundaries of what we thought was possible, offering different displays — or even a combination of displays — depending on a device’s folding state.

Of this new generation of foldable devices, dual-screen devices, which provide symmetrical screens that work together in unique ways, are particularly interesting. But, they also present unique challenges.

A key feature of these foldable, responsive UIs is that screen sizes can change in runtime. This means that applications should recognize screen changes in runtime, which could be a challenging feature for developers to implement who need to focus on business code.

To get around this challenge, Google suggested a new solution: the Jetpack WindowManager. Now that the library is in RC release, Google is encouraging all developers to adopt Jetpack WindowManager with its device-agnostic APIs, testing APIs, and WindowMetrics so you can respond easily to screen changes.

In this post, you’ll learn how to set up the foldable device emulator and how to use the Jetpack WindowManager library to build responsive UIs for Android.

For additional help, check out the links below:

Set Up the Foldable Emulator

To get started, you need to install a foldable emulator. In this tutorial, you will use Microsoft’s Surface Duo 2 emulator to run the demo project. To run the emulator, you will learn how to install and run the foldable emulator on Android Studio.

To get started, you can follow instructions in the resources below:

Note: If you build with other foldable emulators, this project may not work properly on your emulator.

Download the Surface Duo Emulator

You can download the Surface Duo emulator image by following the steps below:

  1. Go to Microsoft Download Center.
  2. Click the Download button on the page.
  3. Choose a download option as in the figure below. You should select the option that matches your PC environment.
  4. Click the Next button and start downloading the emulator file.
Choose your download

Install the SDK and Emulator

After downloading the emulator, follow the instructions below to install it on your PC:

👉 Instructions for Mac:

  1. Open the SurfaceDuoEmulator.dmg file.
  2. Copy the .jar file and the contents of the emulator folder to a new location on your local hard drive. (The emulator folder name cannot contain periods.)
  3. Navigate to the SurfaceDuoEmulator folder where you copied the emulator files.
  4. Run the ./run.sh by double-clicking (or type it on the terminal) to start the installation process. This can take some time - but at the end of the process, you will see the emulator launch.

👉 Window Instructions:

  1. Unzip the downloaded file.
  2. Launch the installer.
  3. After completing the SDK setup, launch the Surface Duo Emulator via the start menu link.

Note: If the emulator does not start, you might need to update the pointer to your Android SDK installation.

Run the Surface Duo 2 Emulator on Android Studio

After running the Surface Duo 2 Emulator, you’ll see the result below on your PC:

Surface Duo 2 result on PC

The Surface Duo 2 emulator will automatically appear in the Android Studio available devices list like the figure below. Then, you can run this demo project on your Duo 2 emulator:

Add emulator to Android Studio

After running this demo project, you’ll see the result below:

Window emulator running

Now, you can control the folding state and degree of the device on the Virtual sensors tab on the emulator controller:

Virtual Sensors Tab

Congratulations! 🎉 Now you can build projects for responsive UIs on the Surface Duo 2 emulator. Now let’s deep dive into the Jetpack WindowManager to see how the responsible application works.

Jetpack WindowManager

The Jetpack WindowManager library makes it possible for application developers to support new device form factors and implement responsive UIs. If you want to use this library in your project, add the following dependency in the build.gradle file for your app:

The Jetpack WindowManager version 1.0.0 contains the following key features:

  • WindowMetricsCalculator: Interface to calculate the WindowMetrics for an Activity. It computes the size and position of the area the window would occupy with MATCH_PARENT width and height.

  • WindowInfoTracker: Provides the WindowLayoutInfo, which contains display features of a window as an observable type such as Flow or RxJava.

  • WindowLayoutInfo: Contains the display features of a window to distinguish whether the window encompasses a fold or hinge.

  • FoldingFeature: Enables you to monitor the folded state of a foldable device to determine device postures.

This article will walk you through the key features of the Jetpack WIndowManager.

Set Up the Example UI

Before deep-diving into the WindowManager API, you need to set up an example layout on your Activity to observe the folded states and display configurations.

First, open the activity_main.xml file and copy-paste the following code:

Next, enables the view binding in the build.gradle file with the following code:

Last, after clicking the sync button on Android Studio, initialize layouts with view binding with the following example:

Calculating the Screen Size with WindowMetricsCalculator

Now, let’s get the window size with the WindowMetricsCalculator. The WindowMetricsCalculator finds the maximum dimensions and position of an area for the window by calculating MATCH_PARENT width and height and any flags that allow the window to extend behind cutout areas.

First, you create an instance of the WindowMetricsCaculator by using the getOrCreate() static method:

Next, you can get the WindowMetrics, which contains the size information of the Window:

After building, you’ll get the following result:

Flipped:

Unspanned

Spanned:

Spanned

Tracking Window With WindowInfoTracker

The WindowManager API provides tracking interfaces of the WindowLayoutInfo, which contains the list of DisplayFeatures located within the window. We can observe the window layout changes by using the windowLayoutInfo(activity) method, which provides the Flow of WindowLayoutInfo across Activity recreations with the code below:

The windowLayoutInfo(activity) method returns Flow<WindowLayoutInfo>. We can observe the WindowLayoutInfo in our Activity with the code below:

Building a Responsive Screen With FoldingFeature

The WindowManager API provides you with FoldingFeature, which describes folds and hinges in the display. Its API provides access to important informatio related to the device methods below:

  • state(): Represents the current posture state of the foldable device such as STATE_FLAT and STATE_HALF_OPENED.

  • isSeparating(): Determines if a FoldingFeature should split the window into multiple physical areas that users will view as logically separate.

  • orientation(): Returns FoldingFeature.Orientation.HORIZONTAL if the FoldingFeature width is greater than the height, or FoldingFeature.Orientation.VERTICAL otherwise.

FoldingFeature provides two folding states: FLAT and HALF_OPENED. FLAT means the posture state should be opened entirely flat, and HALF_OPENDED means two logical screen areas should be folded in between 30 and 150 degrees as seen in the figure below:

Overview

FoldingFeature includes information like hinge orientation and the posture state which you’ve handled above, so we can use these values to check if the device is in tabletop mode or in book mode.

TableTop Mode

Tabletop mode

You can check if the device is in tabletop mode (half open with the hinge horizontal) with the code below:

Book Mode

Book mode

You can check if the device is in book mode (half open with the hinge vertical) with the code below:

👉 You can find the full sample code in this post on GitHub.

For more information, you can also check out the references below:

Using Jetpack WindowManager in Your App

Let’s take a look at how you can use these APIs in our next application. In the screenshot below, you’ll see the the real-world example of the responsive UI with Jetpack WindowManager:

WindowManager Chat Compose example

Foldable devices can have flexible screen sizes depending on folded states, so we can build responsive layouts based on specific breakpoints following the different screens. Google's Material Design suggests some breakpoint guidelines about how content reflows on different screens.

Following the Material Design guidance, we can calculate the screen sizes and define breakpoints of the folding states with the code below:

If you're using Jetpack Compose to build UIs on your app, you can observe the screen size with the following example:

The rememberWindowSizeClass() stores the WindowSize object in the Composition, and it will be updated if screen size changes. So you can build the responsive UIs with the example below:

👉 You can find the full sample codes in this post on GitHub. This real-world example was built with Stream's Jetpack Compose API, so check it out if you have further interest.

Conclusion

In this article, you learned how to set up the foldable emulator and the Jetpack WindowManager to build responsive UIs. Foldable devices provide a larger screen, which provides a more immersive user experience that improves productivity. So you can provide better user experiences by supporting responsive UIs for your Android application.

Again, you can find the full sample codes with examples in this article on GitHub.

To learn more about the foldable devices and how to use them, take a look at this Learn about foldables piece.

In case you have any feedback on this article, reach the team on Twitter @getstream_io or the author
@github_skydoves.

And as always, happy coding!

Jaewoong