Revisiting Jetpack WindowManager
Tags: android, jetpack, kotlin, foldable
Published: 2021-08-22 14:55:18

About a year ago I wrote a short piece called [On supporting foldables](https://dev.to/tkuenneth/on-supporting-foldables-4537). Besides reminiscing about an obscure and obsolete class of the Android framework, [ActivityGroup](https://developer.android.com/reference/android/app/ActivityGroup), I briefly introduced you to [Jetpack WindowManager](https://developer.android.com/jetpack/androidx/releases/window). The version I used was 1.0.0-alpha01. Quite a few things have changed since then. So, time to revisit.

To use Jetpack WindowManager, you need to add it as an implementation dependency in the module-level *build.gradle* file:

```groovy
dependencies {
  ...
  implementation "androidx.window:window:1.0.0-beta01"
  ...
``` 

After that, you can access a new package `androidx.window.layout`. It contains just a new interfaces and classes. The main interaction point is `WindowInfoRepository`, an interface. Now you may be thinking *Wait. How should I interact with an interface?* The source file contains a companion object, which has an extension function to `Activity`. The documentation of `windowInfoRepository()` [says](https://developer.android.com/reference/androidx/window/layout/WindowInfoRepository):

> Provide an instance of `WindowInfoRepository` that is associated
> to the given `Activity`

So now that we know how to access an instance of `WindowInfoRepository`, let's see what to do with it.

### Obtaining window metrics

`currentWindowMetrics` returns a `Flow` for consuming the current `WindowMetrics` according to the current system state.

The [documentation](https://developer.android.com/reference/androidx/window/layout/WindowInfoRepository#currentWindowMetrics()) explains:

> The metrics describe the size of the area the window would
> occupy with `MATCH_PARENT` width and height and any combination
> of flags that would allow the window to extend behind display
> cutouts.
>
> The value of this is based on the current windowing state of the
> system. For example, for activities in multi-window mode, the
> metrics returned are based on the current bounds that the user
> has selected for the `Activity`'s window.

`WindowMetrics` also belongs to package `androidx.window.layout`. Its `bounds` returns a `Rect` which describes the bounds of the area the window occupies.

Now let's return to `WindowInfoRepository`. `windowLayoutInfo` returns a `Flow` of `WindowLayoutInfo` that contains *all available features*.

### Display features

The interface `DisplayFeature` is a description of a physical feature on the display. The documentation [says](https://developer.android.com/reference/androidx/window/layout/DisplayFeature):

> A display feature is a distinctive physical attribute located
> within the display panel of the device. It can intrude into the
> application window space and create a visual distortion, visual
> or touch discontinuity, make some area invisible or create a
> logical divider or separation in the screen space.

Currently, there is only `FoldingFeature`. The documentation [says](https://developer.android.com/reference/androidx/window/layout/FoldingFeature):

> A feature that describes a fold in the flexible display or a
> hinge between two physical display panels.

We can find out quite a few interesting things about the fold:

- `occlusionType` represents how the hinge might occlude content: `NONE`, `FULL`
- `orientation` represents the axis for which the `FoldingFeature` runs parallel to
- `state` represents the state of the `FoldingFeature`: `FLAT`, `HALF_OPENED`
- `isSeparating` calculates [if...](https://developer.android.com/reference/androidx/window/layout/FoldingFeature#isSeparating())

> a `FoldingFeature` should be thought of as splitting the window
> into multiple physical areas that can be seen by users as
> logically separate.

Here's how to make use of this in code. You can find the app on [GitHub](https://github.com/tkuenneth/android-demos/tree/master/misc/WindowManagerDemo2).

```kotlin
class MainActivity : AppCompatActivity() {

  private lateinit var binding: MainBinding

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = MainBinding.inflate(layoutInflater)
    setContentView(binding.root)
    val repo = windowInfoRepository()
    lifecycleScope.launch {
      repo.currentWindowMetrics.collect { windowMetrics ->
        output("Bounds: ${windowMetrics.bounds.toShortString()}")
      }
    }
    lifecycleScope.launch {
      repo.windowLayoutInfo.collect {
        it.displayFeatures.forEach { displayFeature ->
          (displayFeature as FoldingFeature).run {
            output("occlusionType: $occlusionType")
            output("orientation: $orientation")
            output("state: $state")
            output("isSeparating: $isSeparating")
          }
        }
      }
    }
  }

  private fun output(s: String) {
    lifecycleScope.launch(Dispatchers.Main) {
      binding.textview.append("$s\n")
    }
  }
}
```

![Android Emulator configured as a foldable device](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2isnvvuh1szps2dqu1hk.png)


### Summary

A year ago I praised Microsoft for supporting Jetpack WindowManager with their Surface Duo:

> So far Jetpack Window Manager is quite small, yet extremely
> useful. It is a great relief that Microsoft seems to be
> utilizing this, because if there is something our ecosystem
> should not face is fragmentation the the foldable area. Things
> might have been more easy if Google had not chosen to completely
> abandon the tablet market. And a foldable by Googles seems not
> to be on the horizon either.

It looks like Google has finally [rediscovered its love for large screens and foldables](https://developer.android.com/large-screens?gclid=CjwKCAjw64eJBhAGEiwABr9o2HWw2u7sxHI9wIWHKzj40liahkhLLS3Xvayo4_ci73B2pAs2D4Nr0hoCeXwQAvD_BwE). Other vendors are pushing the foldable experience, too. And we might even get a Pixel Fold...