DEV Community

Cover image for Revisiting Jetpack WindowManager
Thomas Künneth
Thomas Künneth

Posted on

Revisiting Jetpack WindowManager

About a year ago I wrote a short piece called On supporting foldables. Besides reminiscing about an obscure and obsolete class of the Android framework, ActivityGroup, I briefly introduced you to Jetpack WindowManager. 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:

dependencies {
  ...
  implementation "androidx.window:window:1.0.0-beta01"
  ...
Enter fullscreen mode Exit fullscreen mode

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:

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 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:

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:

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...

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.

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")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Android Emulator configured as a foldable device

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. Other vendors are pushing the foldable experience, too. And we might even get a Pixel Fold...

Discussion (0)