Large unfolded displays and unique folded states enable new user experiences on
foldable devices. To make your app fold aware, use the [Jetpack WindowManager
library](https://developer.android.com/jetpack/androidx/releases/window), which provides an API surface for foldable device window features
such as folds and hinges. When your app is fold aware, it can adapt its layout
to avoid placing important content in the area of folds or hinges and use folds
and hinges as natural separators.

Understanding whether a device supports configurations such as tabletop or book
posture can guide decisions about supporting different layouts or providing
specific features.

## Window information

The [`WindowInfoTracker`](https://developer.android.com/reference/kotlin/androidx/window/layout/WindowInfoTracker) interface in Jetpack WindowManager exposes window
layout information. The interface's [`windowLayoutInfo()`](https://developer.android.com/reference/kotlin/androidx/window/layout/WindowInfoTracker#windowLayoutInfo(android.app.Activity)) method returns a
stream of [`WindowLayoutInfo`](https://developer.android.com/reference/kotlin/androidx/window/layout/WindowLayoutInfo) data that informs your app about a foldable
device's fold state. The [`WindowInfoTracker#getOrCreate()`](https://developer.android.com/reference/kotlin/androidx/window/layout/WindowInfoTracker#getOrCreate(android.content.Context)) method creates an
instance of `WindowInfoTracker`.

WindowManager provides support for collecting `WindowLayoutInfo` data using
Kotlin flows and Java callbacks.

### Kotlin flows

To start and stop `WindowLayoutInfo` data collection, you can use a [restartable
lifecycle-aware coroutine](https://developer.android.com/topic/libraries/architecture/coroutines#restart) in which the `repeatOnLifecycle` code block is
executed when the lifecycle is at least `STARTED` and is stopped when the
lifecycle is `STOPPED`. Execution of the code block is automatically restarted
when the lifecycle is `STARTED` again. In the following example, the code block
collects and uses `WindowLayoutInfo` data:  

    class DisplayFeaturesActivity : AppCompatActivity() {

        private lateinit var binding: ActivityDisplayFeaturesBinding

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
            setContentView(binding.root)

            lifecycleScope.launch(Dispatchers.Main) {
                lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                    WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                        .windowLayoutInfo(this@DisplayFeaturesActivity)
                        .collect { newLayoutInfo ->
                            // Use newLayoutInfo to update the layout.
                        }
                }
            }
        }
    }

### Java callbacks

The callback compatibility layer included in the
[`androidx.window:window-java`](https://developer.android.com/jetpack/androidx/releases/window#declaring_dependencies) dependency enables you to collect
`WindowLayoutInfo` updates without using a Kotlin flow. The artifact includes
the [`WindowInfoTrackerCallbackAdapter`](https://developer.android.com/reference/kotlin/androidx/window/java/layout/WindowInfoTrackerCallbackAdapter) class, which adapts a
`WindowInfoTracker` to support registering (and unregistering) callbacks to
receive `WindowLayoutInfo` updates, for example:  

    public class SplitLayoutActivity extends AppCompatActivity {

        private WindowInfoTrackerCallbackAdapter windowInfoTracker;
        private ActivitySplitLayoutBinding binding;
        private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

       @Override
       protected void onCreate(@Nullable Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);

           binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
           setContentView(binding.getRoot());

           windowInfoTracker =
                    new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
       }

       @Override
       protected void onStart() {
           super.onStart();
           windowInfoTracker.addWindowLayoutInfoListener(
                    this, Runnable::run, layoutStateChangeCallback);
       }

       @Override
       protected void onStop() {
           super.onStop();
           windowInfoTracker
               .removeWindowLayoutInfoListener(layoutStateChangeCallback);
       }

       class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
           @Override
           public void accept(WindowLayoutInfo newLayoutInfo) {
               SplitLayoutActivity.this.runOnUiThread( () -> {
                   // Use newLayoutInfo to update the layout.
               });
           }
       }
    }

### RxJava support

If you're already using [`RxJava`](https://github.com/ReactiveX/RxJava) (version [`2`](https://github.com/ReactiveX/RxJava#version-2x) or [`3`](https://github.com/ReactiveX/RxJava#version-3x-javadoc)),
you can take advantage of artifacts that enable you to use an
[`Observable`](http://reactivex.io/documentation/observable.html) or [`Flowable`](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Flowable.html)
to collect `WindowLayoutInfo` updates without using a Kotlin flow.

The compatibility layer provided by the `androidx.window:window-rxjava2` and
`androidx.window:window-rxjava3` dependencies includes the
[`WindowInfoTracker#windowLayoutInfoFlowable()`](https://developer.android.com/reference/kotlin/androidx/window/rxjava2/layout/package-summary#(androidx.window.layout.WindowInfoTracker).windowLayoutInfoFlowable(android.app.Activity)) and
[`WindowInfoTracker#windowLayoutInfoObservable()`](https://developer.android.com/reference/kotlin/androidx/window/rxjava2/layout/package-summary#(androidx.window.layout.WindowInfoTracker).windowLayoutInfoObservable(android.app.Activity)) methods, which enable your
app to receive `WindowLayoutInfo` updates, for example:  

    class RxActivity: AppCompatActivity {

        private lateinit var binding: ActivityRxBinding

        private var disposable: Disposable? = null
        private lateinit var observable: Observable<WindowLayoutInfo>

       @Override
       protected void onCreate(@Nullable Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);

           binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
           setContentView(binding.getRoot());

            // Create a new observable.
            observable = WindowInfoTracker.getOrCreate(this@RxActivity)
                .windowLayoutInfoObservable(this@RxActivity)
       }

       @Override
       protected void onStart() {
           super.onStart();

            // Subscribe to receive WindowLayoutInfo updates.
            disposable?.dispose()
            disposable = observable
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { newLayoutInfo ->
                // Use newLayoutInfo to update the layout.
            }
       }

       @Override
       protected void onStop() {
           super.onStop();

            // Dispose of the WindowLayoutInfo observable.
            disposable?.dispose()
       }
    }

## Features of foldable displays

The `WindowLayoutInfo` class of Jetpack WindowManager makes the features of a
display window available as a list of [`DisplayFeature`](https://developer.android.com/reference/kotlin/androidx/window/layout/DisplayFeature) elements.

A [`FoldingFeature`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature) is a type of `DisplayFeature` that provides information
about foldable displays, including the following:

- `state`: The folded state of the device, [`FLAT`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.State#FLAT()) or [`HALF_OPENED`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.State#HALF_OPENED())

- `orientation`: The orientation of the fold or hinge, [`HORIZONTAL`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.Orientation#HORIZONTAL()) or
  [`VERTICAL`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.Orientation#VERTICAL())

- `occlusionType`: Whether the fold or hinge conceals part of the display,
  [`NONE`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.OcclusionType#NONE()) or [`FULL`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.OcclusionType#FULL())

- `isSeparating`: Whether the fold or hinge creates two logical display areas,
  true or false

| **Note:** Although the hinge on foldable devices allows the device to fold to various angles, `FoldingFeature` does not expose the angle as part of the API. Different devices have different reporting ranges, and sensor accuracy can vary from device to device; so, animations or logic based on the precise hinge angle must be tuned to the device.

A foldable device that is `HALF_OPENED` always reports `isSeparating` as true
because the screen is separated into two display areas. Also, `isSeparating` is
always true on a dual‑screen device when the application spans both
screens.

The `FoldingFeature` [`bounds`](https://developer.android.com/reference/kotlin/androidx/window/layout/DisplayFeature#bounds()) property (inherited from `DisplayFeature`)
represents the bounding rectangle of a folding feature such as a fold or hinge.
The bounds can be used to position elements on screen relative to the feature:  

### Kotlin

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }

        }
    }
}
```

### Java

```java
private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}
```

### Tabletop posture

Using the information included in the `FoldingFeature` object, your app can
support postures like tabletop, where the phone sits on a surface, the hinge is
in a horizontal position, and the foldable screen is half opened.

Tabletop posture offers users the convenience of operating their phones without
holding the phone in their hands. Tabletop posture is great for watching media,
taking photos, and making video calls.
![](https://developer.android.com/static/images/large-screens/gallery/media/tabletop.png) **Figure 1.** A video player app in tabletop posture.

Use [`FoldingFeature.State`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.State) and [`FoldingFeature.Orientation`](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature.Orientation) to determine
whether the device is in tabletop posture:  

### Kotlin

```kotlin
fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}
```

### Java

```java
boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}
```

Once you know the device is in tabletop posture, update your app layout
accordingly. For media apps, that typically means placing the playback above the
fold and positioning controls and supplementary content just below for a
hands‑free viewing or listening experience.

On Android 15 (API level 35) and higher, you can invoke a synchronous API to
detect whether a device supports tabletop posture regardless of the current
state of the device.
| **Note:** The API requires [WindowManager Extensions](https://source.android.com/docs/core/display/windowmanager-extensions) version 6, which ships with Android 15 or higher.

The API provides a list of postures supported by the device. If the list
contains tabletop posture, you can split your app layout to support the posture
and run A/B tests on your app UI for tabletop and full‑screen layouts.  

### Kotlin

```kotlin
if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}
```

### Java

```java
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}
```
| **Note:** The function returns only the `TABLETOP` posture, but you can use the hinge sensor orientation to determine whether the device is horizontal (tabletop posture) or vertical (book posture).

#### Examples

- [`MediaPlayerActivity`](https://github.com/android/platform-samples/blob/main/samples/user-interface/windowmanager/src/main/java/com/example/platform/ui/windowmanager/MediaPlayerActivity.kt) app: See how to use [Media3
  Exoplayer](https://developer.android.com/guide/topics/media/exoplayer) and [WindowManager](https://github.com/android/platform-samples/tree/main/samples/user-interface/windowmanager) to create a fold‑aware video
  player.

- [Optimize your camera app on foldable devices with Jetpack WindowManager](https://developer.android.com/codelabs/android-camera-foldables#5)
  codelab: Learn how to implement tabletop posture for photography apps. Show
  the viewfinder on the top half of the screen (above the fold) and the
  controls on the bottom half (below the fold).

### Book posture

Another unique foldable feature is book posture, where the device is half opened
and the hinge is vertical. Book posture is great for reading e‑books. With
a two‑page layout on a large screen foldable open like a bound book, book
posture captures the experience of reading a real book.

It can also be used for photography if you want to capture a different aspect
ratio while taking pictures hands‑free.

Implement book posture with the same techniques used for tabletop posture. The
only difference is the code should check that the folding feature orientation is
vertical instead of horizontal:  

### Kotlin

```kotlin
fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}
```

### Java

```java
boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}
```
| **Note:** On foldable devices that have two screens separated by a hinge, use layouts designed for tabletop and book postures even if the `FoldingFeature.State` is `FLAT`. Don't place UI controls too close to a fold or hinge when `isSeparating` is true because the controls can be difficult to reach. Use `occlusionType` to decide whether to place content within the folding feature `bounds`.

## Window size changes

An app's display area can change as a result of a device configuration change,
for example, when the device is folded or unfolded, rotated, or a window is
resized in multi‑window mode.

The Jetpack WindowManager [`WindowMetricsCalculator`](https://developer.android.com/reference/kotlin/androidx/window/layout/WindowMetricsCalculator) class enables you to
retrieve the current and maximum window metrics. Like the platform
[`WindowMetrics`](https://developer.android.com/reference/kotlin/android/view/WindowMetrics) introduced in API level 30, the [WindowManager
`WindowMetrics`](https://developer.android.com/reference/kotlin/androidx/window/layout/WindowMetrics) provide the window bounds, but the API is backward compatible
down to API level 14.

See [Use window size classes](https://developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size-classes).

## Additional resources

### Samples

- Jetpack [WindowManager](https://github.com/android/platform-samples/tree/main/samples/user-interface/windowmanager): Example of how to use the Jetpack WindowManager library
- [Jetcaster](https://github.com/android/compose-samples/tree/main/Jetcaster) : Tabletop posture implementation with Compose

### Codelabs

- [Support foldable and dual-screen devices with Jetpack WindowManager](https://developer.android.com/codelabs/android-window-manager-dual-screen-foldables#0)
- [Optimize your camera app on foldable devices with Jetpack WindowManager](https://developer.android.com/codelabs/android-camera-foldables#0)