Tag Archives: windowmanager

Jetpack WindowManager 1.1 is stable!

Posted by Francesco Romano, Developer Relations Engineer on Android

It’s been more than a year since the release of the Jetpack WindowManager 1.0 stable version, and many things have happened in the foldables and large screen space. Many new devices have entered the market, and many new use cases have been unlocked!

Jetpack WindowManager is one of the most important libraries for optimizing your Android app for different form factors. And this release is a major milestone that includes a number of new features and improvements.

Let’s recap all the use cases covered by the Jetpack WindowManager library.

Get window metrics (and size classes!)

Historically, developers relied on the device display size to decide the layout of their apps, but with the availability of different form factors (such as foldables) and display modes (such as multi-window and multi-display) information about the size of the app window rather than the device display has become essential.

The Jetpack WindowManager WindowMetricsCalculator interface provides the source of truth to measure how much screen space is currently available for your app.

Built on top of that, the window size classes are a set of opinionated viewport breakpoints that help you design, develop, and test responsive and adaptive application layouts. The breakpoints have been chosen specifically to balance layout simplicity with the flexibility to optimize your app for unique cases.

With Jetpack Compose, use window size classes by importing them from the androidx.compose.material3 library, which uses WindowMetricsCalculator internally.

For View-based app, you can use the following code snippet to compute the window size classes:

private fun computeWindowSizeClasses() { val metrics = WindowMetricsCalculator.getOrCreate() .computeCurrentWindowMetrics(this) val widthDp = metrics.bounds.width() / resources.displayMetrics.density val widthWindowSizeClass = when { widthDp < 600f -> WindowSizeClass.COMPACT widthDp < 840f -> WindowSizeClass.MEDIUM else -> WindowSizeClass.EXPANDED } val heightDp = metrics.bounds.height() / resources.displayMetrics.density val heightWindowSizeClass = when { heightDp < 480f -> WindowSizeClass.COMPACT heightDp < 900f -> WindowSizeClass.MEDIUM else -> WindowSizeClass.EXPANDED } }

To learn more, see our Support different screen sizes developer guide.

Make your app fold aware

Jetpack WindowManager also provides all the APIs you need to optimize the layout for foldable devices.

In particular, use WindowInfoTracker to query FoldingFeature information, such as:

  • state: The folded state of the device, FLAT or HALF_OPENED
  • orientation: The orientation of the fold or device hinge, HORIZONTAL or VERTICAL
  • occlusion type: Whether the fold or hinge conceals part of the display, NONE or FULL
  • is separating: Whether the fold or hinge creates two logical display areas, true or false
  • bounds: The bounding rectangle of the feature within the application window (inherited from DisplayFeature)

You can access this data through a Flow:

override fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate(this@MainActivity) .windowLayoutInfo(this@MainActivity) .collect { layoutInfo -> // New posture information val foldingFeature = layoutInfo.displayFeatures // use the folding feature to update the layout } } } }

Once you collect the FoldingFeature info, you can use the data to create an optimized layout for the current device state, for example, by implementing tabletop mode! You can see a tabletop mode example in MediaPlayerActivity.kt.

A great place to start learning about foldables is our codelab: Support foldable and dual-screen devices with Jetpack WindowManager.

Show two Activities side by side

Last, but not least, you can use the latest stable Jetpack WindowManager API: activity embedding.

Available since Android 12L, activity embedding enables developers with legacy multi-activiity architectures to display multiple activities from the same application—or even from multiple applications—side-by-side on large screens.

It’s a great way to implement list-detail layouts with minimal or no code changes.

Note: Modern Android Development (MAD) recommends using a single-activity architecture based on Jetpack APIs, including Jetpack Compose. If your app uses fragments, check out SlidingPaneLayout. Activity embedding is designed for multiple-activity, legacy apps that can't be easily updated to MAD.

It is also the biggest change in the library, as the activity embedding APIs are now stable in 1.1!

Not only that, but the API is now richer in features, as it enables you to:

  • Modify the behavior of the split screen (split ratio, rules, finishing behavior)
  • Define placeholders
  • Check (and change) the split state at runtime
  • Implement horizontal splits
  • Start a modal in full window

Interested in exploring activity embedding? We’ve got you covered with a dedicated codelab: Build a list-detail layout with activity embedding.

Many apps are already using activity embedding in production, for example, WhatsApp:

Image of WhatsApp on a large screen device showing activity embedding

And ebay!

Image of Ebay on a large screen device showing activity embedding

Implementing list-details layouts with multiple activities is not the only use case of activity embedding!

Starting from Android 13 (API level 33), apps can embed activities from other apps.

Cross‑application activity embedding enables visual integration of activities from multiple Android applications. The system displays an activity of the host app and an embedded activity from another app on screen side by side or top and bottom, just as in single-app activity embedding.

Host apps implement cross-app activity embedding the same way they implement single-app activity embedding, but the embedded app must opt-in for security reasons.

You can learn more about cross-application embedding in the Activity embedding developer guide.

Conclusion

Jetpack WindowManager is one of the most important libraries you should learn if you want to optimize your app’s user experience for different form factors.

WindowManager is also adding new, interesting features with every release, so keep an eye out for what’s coming in version 1.2.

See the Jetpack WindowManager documentation and sample app to get started with WindowManager today!

What’s new in WindowManager 1.1.0-beta01

Posted by Jon Eckenrode, Technical Writer, Software Engineering blog header featuring Android logos

The 1.1.0-beta01 release of Jetpack WindowManager continues the library’s steady progress toward stable release of version 1.1.0. The beta adds an assortment of new features and capabilities, which are ready for testing and early adoption today!

We need your feedback so we can make WindowManager work best for you. Add the 1.1.0-beta01 dependency to your app, follow the migration steps below (if you’re already using a previous version of the library), and let us know what you think!

Activity embedding

androidx.window.embedding

Activity embedding enables you to optimize your multi-activity apps for large screens. The 1.1.0-beta01 release augments and refactors the APIs to provide greater versatility, capability, and control in managing task window splits. We started with experimental APIs in 1.0.0 and are promoting them ultimately to stable in 1.1.0.

tl;dr

Added a manifest setting so you can inform the system your app has implemented activity embedding. Refactored SplitController to be more focused on split properties; extracted split rule APIs to RuleController and activity embedding APIs to ActivityEmbeddingController. Added the SplitAttributes class to describe embedding splits. Added the EmbeddingAspectRatio class to set a minimum ratio for applying activity embedding rules. Changed pixels units to display-independent pixels (dp). Enabled customization of split layouts. Added a tag to rules so that developers can identify and manage specific rules.

What’s new

PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED

  • Added as a boolean property of the <application> tag in the app manifest.

ActivityEmbeddingController

  • Added class for operations related to the Activity or ActivityStack classes.

  • Includes isActivityEmbedded() to replace the API in SplitController.

RuleController

  • Added class for operations related to the EmbeddingRule class and subclasses.
  • Includes the following APIs to replace APIs in SplitController:
    • addRule() — Adds a rule or updates the rule that has the same tag.
    • removeRule() — Removes a rule from the collection of registered rules.
    • setRules() — Establishes a collection of rules.
    • clearRules() — Removes all registered rules.
    • parseRules() — Parses rules from XML rule definitions.

          SplitAttributes

          • Added class to define the split layout.

          EmbeddingAspectRatio

          • Added class to define enum-like behavior constants related to display aspect ratio. Lets you specify when splits are enabled based on the parent window’s aspect ratio.

          See SplitRule for properties that use the constants.


          What’s changed

          EmbeddingRule

          • Added tag field for identification of split rules.

          SplitController
          • Refactored APIs to the following modules:
            • ActivityEmbeddingController
              • Moved isActivityEmbedded() to ActivityEmbeddingController.
            • RuleController
              • Removed the following APIs and replaced their functionality with RuleController APIs:
                • clearRegisteredRules()
                • getSplitRules()
                • initialize()
                • registerRule()
                • unregisterRule()
            • Deprecated isSplitSupported() method and replaced with splitSupportStatus property to provide more detailed information about why the split feature is not available.

            • The getInstance() method now has a Context parameter.

                    Note: The getInstance() methods of ActivityEmbeddingController and RuleController also have a Context parameter.
                    • Added SplitAttributes calculator functions to customize split layouts:
                      • setSplitAttributesCalculator()
                      • clearSplitAttributesCalculator()
                      • isSplitAttributesCalculatorSupported() to check whether the SplitAttributesCalculator APIs are supported on the device.

                    • Defined SplitSupportStatus nested class to provide state constants for the splitSupportStatus property. Enables you to modify app behavior based on whether activity embedding splits are supported in the current app environment.

                    SplitRule

                    • Added defaultSplitAttributes property which defines the default layout of a split; replaces splitRatio and layoutDirection.
                    • Added translation of the XML properties splitRatio and splitLayoutDirection to defaultSplitAttributes.
                    • Changed minimum dimension definitions to use density-independent pixels (dp) instead of pixels.
                      • Changed minWidth to minWidthDp with default value 600dp.
                      • Changed minSmallestWidth to minSmallestWidthDp with default value 600dp.
                      • Added minHeightDp property with default value 600dp.
                    • Added maxAspectRatioInHorizontal with default value ALWAYS_ALLOW.
                    • Added maxAspectRatioInPortrait with default value 1.4.
                    • Defined FinishBehavior nested class to replace finish behavior constants.
                    • Applied the property changes to the Builder nested class of SplitPairRule and SplitPlaceholderRule.

                        SplitInfo

                        • Replaced getSplitRatio() with getSplitAttributes() to provide additional split-related information.

                        Window layout

                        androidx.window.layout

                        The window layout library lets you determine features of app display windows. With the 1.1.0-beta01 release, you can now work in contexts other than just activities.

                        What’s changed

                        WindowInfoTracker

                        • Added non-activity UI context support in experimental.

                        WindowMetricsCalculator

                        • Added non-activity UI context support.

                        Migration steps

                        Take the next step and upgrade your app from a previous alpha version. And please let us know how we can further facilitate the upgrade process.

                        PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
                        • To enable activity embedding, apps must add the property to the <application> tag in the app manifest: 

                        < property android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" android:value="true" />

                        When the property is set to true, the system can optimize split behavior for the app early.


                        SplitInfo

                        • Check if the current split is stacked:

                        SplitInfo.splitAttributes.splitType is SplitAttributes.SplitType.ExpandContainersSplitType

                        • Check the current ratio:

                        if (SplitInfo.splitAttributes.splitType is SplitAttributes.SplitType.RatioSplitType) { val ratio = splitInfo.splitAttributes.splitType.ratio } else { // Ratio is meaningless for other types. }
                         

                        SplitController

                        • SplitController.getInstance()

                        changes to

                            SplitController.getInstance(Context)

                        • SplitController.initialize(Context, @ResId int)

                        changes to:

                        RuleController.getInstance(Context) .setRules(RuleController.parse(Context, @ResId int)) 
                        • SplitController.getInstance().isActivityEmbedded(Activity)
                        changes to:

                        ActivityEmbeddingController.getInstance(Context) .isActivityEmbedded(Activity)

                        • SplitController.getInstance().registerRule(rule)
                                  changes to:
                        RuleController.getInstance(Context).addRule(rule)

                        • SplitController.getInstance().unregisterRule(rule)

                        changes to:
                        RuleController.getInstance(Context).removeRule(rule)
                        • SplitController.getInstance().clearRegisteredRules()

                        changes to:
                        RuleController.getInstance(Context).clearRules()
                        • SplitController.getInstance().getSplitRules()

                        changes to:

                            RuleController.getInstance(Context).getRules() 


                        SplitRule

                        • Change minWidth to minWidthDp and minSmallestWidth to minSmallestWidthDp
                        • minWidthDp and minSmallestWidthDp now use dp units instead of pixels

                        Apps can use the following call:

                        TypedValue.applyDimension( COMPLEX_UNIT_DIP, minWidthInPixels, resources.displayMetrics )

                        or simply divide minWithInPixels by displayMetrics#density.  


                        SplitPairRule.Builder

                        • SplitPairRule.Builder( filters, minWidth, minSmallestWidth )

                        changes to:

                        SplitPairRule.Builder(filters) // Optional if minWidthInDp argument is 600. .setMinWidthDp(minWidthInDp) // Optional if minSmallestWidthInDp argument is 600. .setMinSmallestWidthDp(minSmallestWidthInDp)

                        • setLayoutDirection(layoutDirection) and setSplitRatio(ratio)

                        change to:

                        setDefaultSplitAttributes(SplitAttributes.Builder() .setLayoutDirection(layoutDirection) .setSplitType(SplitAttributes.SplitType.ratio(ratio)) .build() )

                        • setFinishPrimaryWithSecondary and setFinishSecondaryWithPrimary take the FinishBehavior enum-like constants.

                        See SplitRule migrations for details.

                        • Use:
                        setMaxAspectRatioInPortrait( EmbeddingAspectRatio.ALWAYS_ALLOW )
                        to show splits on portrait devices.

                        SplitPlaceholder.Builder

                        • Has only filters and placeholderIntent parameters; other properties move to setters.
                        See SplitPairRule.Builder migrations for details.  
                        • setFinishPrimaryWithPlaceholder takes the FinishBehavior enum-like constants.
                        See finish behavior migrations for details.

                        • setLayoutDirection(layoutDirection) and setSplitRatio(ratio)

                        change to 

                        setDefaultSplitAttributes(SplitAttributes.Builder() .setLayoutDirection(layoutDirection) .setSplitType(SplitAttributes.SplitType.ratio(ratio)) .build() )

                        See layout direction migrations for details.

                        • Use:

                        setMaxAspectRatioInPortrait( EmbeddingAspectRatio.ALWAYS_ALLOW )

                        to show splits on portrait devices.

                         
                        Finish behavior

                        Finish behavior constants must be migrated to FinishBehavior enum-like class constants:

                        • FINISH_NEVER changes to FinishBehavior.NEVER
                        • FINISH_ALWAYS changes to FinishBehavior.ALWAYS
                        • FINISH_ADJACENT changes to FinishBehavior.ADJACENT

                        Layout direction

                        Layout direction must be migrated to SplitAttributes.LayoutDirection:

                        • ltr changes to SplitAttributes.LayoutDirection.LEFT_TO_RIGHT
                        • rtl changes to SplitAttributes.LayoutDirection.RIGHT_TO_LEFT
                        • locale changes to SplitAttributes.LayoutDirection.LOCALE
                        • splitRatio migrates to SplitAttributes.SplitType.ratio(splitRatio)


                        Get started 

                        To get started with WindowManager, add the Google Maven repository to your app’s settings.gradle or project-level build.gradle file: 


                        dependencyResolutionManagement {

                            repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

                            repositories {

                                google()

                            }

                        }

                         Then add the 1.1.0-beta01 dependency to your app’s module-level build.gradle file: 


                        dependencies {

                            implementation 'androidx.window:window:1.1.0-beta01'

                            . . .

                        }

                         Happy coding!

                        Introducing Camera Viewfinder

                        Posted by Francesco Romano, Developer Relations Engineer, Androidhand holding a phoneOver the years, Android devices have evolved to include a variety of sizes, shapes, and displays, among other features. Since the beginning, however, taking pictures with your phone has been one of the most important use cases. Today, camera capabilities are still one of the top reasons consumers purchase a phone.

                        As a developer, you want to leverage camera capabilities in your app, so you decide to adopt the Android Camera Framework. The first use case you want to implement is the Preview use case, which shows the output of the camera sensor on the screen.

                        So you go ahead and create a CaptureSession using a surface as big as the screen size. As long as the screen has the same aspect ratio as the camera sensor output and the device stays in its natural portrait orientation, everything should be fine.

                        But what happens when you resize the window, unfold your device, or change the display or orientation? Well, in most cases, the preview may appear stretched, upside down, or incorrectly rotated. And if you are in a multi-window environment, your app may even crash.

                        Why does this happen? Because of the implicit assumptions you made while creating the CaptureSession.

                        Historically, your app could live in the same window for its whole life cycle, but with the availability of new form factors such as foldable devices, and new display modes such as multi-window and multi-display, you can't assume this will be true anymore.

                        In particular, let's see some of the most important considerations when developing an app targeting various form factors:

                        Let's examine some common pitfalls to avoid when developing an app targeting various form factors:

                        • Don't assume your app will live in a portrait-shaped window. Requesting a fixed orientation is still supported in Android 13, but now device manufacturers may have the option of overriding an app request for a preferred orientation.
                        • Don't assume any fixed dimension or aspect ratio for your app. Even if you set resizableActivity = "false", your app could still be used in multi-window mode on large screens (>=600dp).
                        • Don't assume a fixed relationship between the orientation of the screen and the camera. The Android Compatibility Definition Document specifies that a camera image sensor "MUST be oriented so that the long dimension of the camera aligns with the screen's long dimension." Starting with API level 32, camera clients that query the orientation on foldable devices can receive a value that dynamically changes depending on the device/fold state.
                        • Don't assume the size of the inset can't change. The new taskbar is reported to applications as an inset, and when used with gesture navigation, the taskbar can be hidden and shown dynamically.
                        • Don't assume your app has exclusive access to the camera. While your app is in a multi-window state, other apps can obtain access to shared resources like camera and microphone.

                        While CameraX already handles most of the cases above, implementing a preview that works in different scenarios with Camera2 APIs can be complex, as we describe in the Support resizable surfaces in your camera app Codelab.

                        Wouldn’t it be great to have a simple component that takes care of those details and lets you focus on your specific app logic?

                        Say no more…

                        Introducing CameraViewfinder

                        CameraViewfinder is a new artifact from the Jetpack library that allows you to quickly implement camera previews with minimal effort. It internally uses either a TextureView or SurfaceView to display the camera feed, and applies the required transformations on them to correctly display the viewfinder. This involves correcting their aspect ratio, scale, and rotation. It is fully compatible with your existing Camera2 codebase and continuously tested on several devices.

                        Let’s see how to use it.

                        First, add the dependency in your app-level build.gradle file:

                        implementation "androidx.camera:camera-viewfinder:1.3.0-alpha01"

                        Sync your project. Now you should be able to directly use the CameraViewfinder as any other View. For example, you can add it to your layout file:

                        <androidx.camera.viewfinder.CameraViewfinder
                          android:id="@+id/view_finder"
                          app:scaleType="fitCenter"
                          app:implementationMode="performance"
                          android:layout_width="match_parent"
                          android:layout_height="match_parent"/>

                        As you can see, CameraViewfinder has the same controls available on PreviewView, so you can choose different Implementation modes and scaling types.

                        Now that the component is part of the layout, you can still create a CameraCaptureSession, but instead of providing a TextureView or SurfaceView as target surfaces, use the result of requestSurfaceAsync().

                        fun startCamera(){
                            val previewResolution = Size(width, height)
                            val viewfinderSurfaceRequest =
                        ViewfinderSurfaceRequest(previewResolution, characteristics)
                            val surfaceListenableFuture =
                                cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)

                            Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
                                override fun onSuccess(surface: Surface) {
                                    //create a CaptureSession using this surface as usual
                                }
                                override fun onFailure(t: Throwable) { /* something went wrong */}
                            }, ContextCompat.getMainExecutor(context))
                        }


                        Bonus: optimized layouts for foldable devices

                        CameraViewFinder is ready-to-use across resizable surfaces, configuration changes, rotations, and multi-window modes, and it has been tested on many foldable devices.

                        But if you want to implement optimized layouts for foldable and dual screen devices, you can combine CameraViewFinder with the Jetpack WindowManager library to provide unique experiences for your users.

                        For example, you can choose to avoid showing full screen preview if there is a hinge in the middle of the screen, or if the device is in “book” or “tabletop” mode. In those scenarios, you can have the viewfinder in one portion of the screen and the controls on the other side, or you can use part of the screen to show the last pictures taken. Imagination is the limit!

                        The sample app is already optimized for foldable devices and you can find the code to handle posture changes here. Have a look!