Author Archives:

Enhance your app for the new Pixel lineup: Unveiled at Made by Google

Posted by Fahd Imtiaz, Senior Product Manager, Loryn Hairston, Product Marketing Manager, and Tracy Agyemang, Product Marketing Manager, Android Developer




Made by Google expands what's possible across the Android ecosystem. With the introduction of the Pixel 11 Pro Fold, Pixel Watch 5, and the entire Pixel family, users are moving seamlessly across diverse screen sizes, unique postures, and intelligent experiences. For you, the developer, this represents a massive opportunity: foldable users spend about 14x more than standard phone users. To help you elevate your existing experience without starting from scratch, we’re sharing our latest platform guidance alongside real-world examples from developers already putting these features into production.

Deliver adaptive experiences across foldables and expanded displays

The Pixel 11 Pro Fold gives your app a chance to flex its capabilities with an expanded inner display and a standard size outer screen. Building for the foldable form factor requires dropping hardcoded layout rules and designing around available window space. Leveraging Jetpack Compose APIs like Navigation 3 with Scene strategies or our newest layout APIs like Grid and FlexBox allows your layout containers to automatically wrap, span, and reflow. You can also use the experimental MediaQuery API to dynamically adapt your UI to environmental signals like foldable posture, and keyboard states.

Building adaptively requires tracking actual app dimensions rather than physical device size, especially during split-screen and multitasking flows. Using Window Size Classes from the WindowManager library allows your layout to respect folds and hinges as natural content separators.

For instance, Notability leveraged Material 3 Window Size Classes to create a responsive two-pane layout that transitions smoothly between folded and expanded screens. As Ryan Shea, Android Engineering Manager at Notability, shared, tracking the window itself allows their layout and canvas zoom to ensure notes stay fit to the page through every fold, rotation, or split-screen resize, noting that they wanted the app "to feel native at every size, not just stretched to fit."

Notability’s quiz UI adapted for expanded screens

Ensuring these transitions feel seamless also requires state preservation across configuration changes. Using ViewModel retains UI state so interactions like scroll position, form inputs, and open dialogs remain uninterrupted when transitioning between inner and outer screens.

Taking this approach, Flo Health used Jetpack Compose state primitives, ViewModel, and Window Size Classes to make their highest-traffic user journeys resilient to rotation, fold/unfold and resizing transitions. As Aleksandr Kolodiazhnyi, Senior Android Engineer at Flo Health, shared, “Android's adaptive guidance turned what looked like a major refactor into a templated rollout," allowing them to adopt Compose primitives without a rewrite, "cutting [their] state-preservation code by roughly 30% while fixing lifecycle and analytics correctness issues that improved the app on every form factor."




To take full advantage of the foldable form factor, leverage FoldingFeature updates to trigger posture-specific layouts. When a user partially folds their device into tabletop posture, you can split your UI automatically by placing primary controls on the lower display and main content or viewfinders on the upper display.

Handling camera previews across foldable state changes, requires managing orientation shifts carefully. Migrating to the CameraX library ensures automatic handling of sensor rotation and display scaling across screens, while existing Camera2 codebases can also achieve stability using the CameraViewfinder library. These camera and display capabilities allow you to power dual-screen previewing and high-resolution rear camera selfies with minimal custom logic.

Prepare your app for these form factors today by exploring our complete adaptive development guidance at Build adaptive apps.

Bring delightful, gesture-driven experiences to the wrist

The new Pixel Watch 5 is here, and we’ve optimized it to take advantage of the intelligent, power-efficient, touch-free convenience of Wear OS 7. Thanks to system-wide performance optimizations and a collection of new features built to help users complete tasks efficiently, you can provide rich experiences that require only a single user action to complete.

The one-handed gestures framework provides a convenient way for users to interact with their watches without needing to touch the screen with their opposite hand. Starting with the 1.7 beta release of Compose for Wear OS 7, you can seamlessly integrate one-handed gesture control into your Wear Compose apps with simple physical inputs on the watch-wearing arm, like a double-pinch or wrist turn.

Spotify is adopting this framework to make controlling media more effortless. By mapping Wear OS gesture events directly to the media player state, users will be able to pause or resume playback using a simple double-pinch, keeping music controls accessible even when their hands are full.


Pause Spotify media with a pinch gesture

Wear OS 7 also brings Live Updates directly to the wrist to surface real-time information like live sports scores, workout progress, and delivery status, which can also appear in the At-a-Glance surface on Pixel Watch 5. For example, Just Eat uses Live Updates to keep users informed on order arrival times at a glance. You can publish updates locally from your watch app or leverage phone notification bridging on supported devices to deliver real-time tracking across screens.

Live Updates from Just Eat delivering real-time status and delivery ETAs at a glance

You can also extend glanceable interactions across watch surfaces on Wear OS 7 by using Wear Widgets, powered by Jetpack Glance and RemoteCompose. Wear Widgets with Compose offer greater expressiveness and consistency than the old Tiles framework, and the two available widget layouts—small and large– align perfectly with the 2x1 and 2x2 formats on mobile, ensuring your designs feel cohesive across devices.

On top of all these great new features, Wear OS 7 delivers up to a 10 percent improvement in battery life over Wear OS 6, making the Pixel Watch 5 a truly indispensable all-day companion for your users.

To get started developing for Wear OS 7, use the new emulator, and check out all of our Wear OS resources and guidance at Build apps for the wrist with Wear OS.

Unlock on-device intelligence with Gemini Nano 4


Pixel 11 devices are built to run Gemini Nano 4, bringing fast, responsive, on-device intelligence to the hardware. By running AI workflows directly on device, you can offer low-latency, real-time interactions that feel instant and integrated without needing round trips to the cloud.

Through the ML Kit GenAI Prompt API, you can send natural language requests directly to Gemini Nano on device. The model supports over 140 languages, better multimodal understanding, and much more. Build intelligent on-device features using advanced capabilities like structured output and thinking mode.

Build smart capabilities into your app using our self-service tools and Gemini models.

Shape the next generation of experiences for the Pixel ecosystem today

Made by Google showcases what's possible when hardware and software evolve together, and you are at the center of that innovation. You can begin optimizing your apps today by exploring our updated adaptive guidance, creating glanceable experiences for Wear OS 7, and integrating on-device AI with ML Kit.

To help you implement these updates even faster, you can now leverage Android skills, which provide AI-optimized instructions for agents and tools. Whether you are using Gemini in Android Studio or running the Android CLI through other agents, Android skills give your AI tools the context needed to execute complex workflows automatically. For instance, you can prompt your agent with the CameraX skill to handle camera display scaling across foldables, or use the Adaptive skill to set up dynamic Compose layouts without additional manual work.

Take advantage of these new surfaces, accelerate your workflow with agentic tools, and share your latest builds with the Android community! Head over to developer.android.com to access full documentation, explore the Android skills GitHub repository, and start building today.







Migrate Campaign-level Broad Match and Automatically Created Assets to AI Max

The legacy automigrations are divided into two separate phases:


1. Campaign broad and ACA auto-migration: September 1 – September 30, 2026

On September 1, 2026, Google Ads will begin automatically upgrading Search campaigns using Campaign-level Broad Match (CB) or standalone Automatically Created Assets (ACA) to AI Max. The migration job will roll out progressively throughout the month.

To find the campaigns that have been migrated at any point during the month:

   SELECT
      campaign.id,
      campaign.name,
      campaign.aca_migration_date_time,
      campaign.broad_match_migration_date_time
    FROM campaign
    WHERE campaign.aca_migration_date_time > '1970-01-01 00:00:00'
      AND campaign.broad_match_migration_date_time > '1970-01-01 00:00:00'

Default migrated configurations

To support campaign management continuity and minimize performance volatility, campaigns will be migrated in-place to equivalent AI Max settings.

  • Campaign-Level Broad Match (CB):
    • Text Customization (TC): OPT_OUT (OFF)
    • Final URL Expansion (FUE): OPT_OUT (OFF)
    • Search Term Matching (STM): true (ON) by default
    • Brand Lists: Existing brand inclusions and exclusions are preserved and moved into the new setup in-place.
  • Standalone Automatically Created Assets (ACA):
    • Text Customization (TC): OPT_IN (ON) by default
    • Final URL Expansion (FUE): OPT_OUT (OFF)
    • Search Term Matching (STM): true (ON) by default


2. API creation blocks and legacy cleanup

To prevent the creation of new legacy structures ahead of the auto-migration, strict enforcement blocks are now in place across the developer surfaces:

  • Legacy creation end of life (August 3, 2026): Creating new Campaign-level Broad Match configurations or legacy Automatically Created Assets (ACA) is now blocked across the UI, Google Ads Editor, and all versions of the API.


3. API versioning lifecycle and support grace period

How you manage these features depends on the API version your integration targets:

New API versions (post-September 1, 2026)

Any new versions of the Google Ads API released after September 1, 2026, will completely remove the legacy entities used to create or manage:

Older API versions (grace period)

  • Active support: Previously released API versions that are currently active will temporarily continue to support legacy ACA and Broad Match settings.
  • Sunset timeline: You can continue to edit and manage these legacy setups through older API versions until those specific versions reach their standard scheduled sunset (typically one year after their introduction, around September 2027). At that point, all remaining legacy entities will be permanently removed.


4. Dynamic Search Ads automigration February 2027

Your active legacy DSA campaigns are excluded from the September auto-migrations and will continue to serve as normal. However, prepare for the upcoming DSA timeline:

  • September 2026: Google will launch in-account pre-migration banners in the UI to warn unmigrated advertisers of the upcoming transition and encourage voluntary one-click upgrades by selecting “Upgrade Your Dynamic Search Ads (DSAs) to AI Max” under Campaign Settings | Dynamic Search Ads setting.
  • January 15, 2027: Official MSA reminder notification will be sent to all impacted accounts.
  • February 1–28, 2027: Automigration begins. The ability to create new DSA ad groups will be permanently removed, and unmigrated DSA campaigns will automatically upgrade to standard Search ad groups with AI Max enabled.


Developer action plan

  1. Audit your integration: Identify any scripts, external tools, or client libraries still attempting to create or edit campaign-level broad match settings or legacy ACA.
  2. Move to standard entities: Begin updating your campaign construction workflows to use standard Search campaigns with the AI Max settings bundle (Uber Toggle, Text Customization, and Search Term Matching).
  3. Handle grandfathered read-only entities: Be prepared to handle API error codes if your integration attempts to modify (rather than pause or delete) legacy Expanded Dynamic Search ads (eDSAs) or criteria once they transition to standard ad groups.


Bob Hancock, Google Ads API Team

Chrome Beta for Desktop Update

The Beta channel has been updated to 152.0.7977.42 for Windows, Mac and Linux.

A partial list of changes is available in the Git log. Interested in switching release channels? Find out how. If you find a new issue, please let us know by filing a bug. The community help forum is also a great place to reach out for help or learn about common issues.

Chrome Release Team
Google Chrome

Bring one-handed gestures to your Wear OS app

Posted by Chiara Chiappini, Developer Relation Engineer, Android Developer Relations


One-handed gestures offer a convenient and touch-free way for users to interact with their watches, enabling them to perform key actions using only the hand on which the device is worn. 

First introduced on Pixel Watch with Wear OS 6.1, one-handed gestures made quick interactions effortless, such as starting and stopping a timer, accepting calls, and controlling media. 

Now, with Wear OS 7, we're expanding this functionality with a new Gestures framework that allows OEMs to map gestures to primary actions and dismissals, and an API to bring gesture control to the developer community.

Starting with the 1.7 beta release of Compose for Wear OS, you can seamlessly integrate gesture control into your Wear Compose apps. To use this release, upgrade your Wear Compose dependency to:

androidx.wear.compose:compose-material3:1.7.0-beta01

Designing for one-handed interaction

The one-handed gestures framework is designed around two primary interaction patterns that allow users to take action without touching the screen:

  • Primary action, which on Pixel Watch is mapped to a double-pinch gesture: this action should be mapped to the most important task in a given context. For example, users can perform this gesture to take a photo in a camera app, start/stop a timer, or accept an incoming call. 
  • Dismiss action, which on Pixel Watch is mapped to a wrist turn gesture: this action is mapped to system back by default and provides an intuitive way to close interruptive screens or get back to the watch face. It may be overridden for specific use cases, such as silencing an incoming phone call.

These gestures are currently available on Pixel Watch 3 and newer, and the Wear OS gesture framework is available to all Wear OS device manufactures to adopt.

Check out our new design guidance for integrating one-handed gestures into your Wear app.

Integrating gestures with Compose on Wear OS

To provide seamless gesture support in Wear OS 7, we are introducing a new Modifier.oneHandedGesture that you can apply to any existing interactive composable to make it gesture-aware. 

Implementing gestures with Compose on Wear OS requires these steps:

  1. Define the gesture configuration. Start by using rememberOneHandedGestureConfiguration to define the nature of the interaction. This configuration dictates the basic behavior by providing the GestureAction (e.g. tracking a primary pinch or a dismiss wrist flick).
  2. Initialize the indicator state. Depending on your UI component, initialize a specific state object, such as OneHandedGestureClickIndicatorState for buttons or OneHandedGestureScrollIndicatorState for scrollable lists. This state is used to coordinate visual feedback between the gesture detection modifier and the visual UI indicators, seamlessly managing visibility, timing, and animations.
  3. Apply Modifier.oneHandedGesture to your interactive component. You'll pass in your configuration and state, and you’ll provide standard callbacks: onGestureAvailable to activate the visual hint when the system prepares the gesture, and onGesture to execute your action when the gesture happens.

The following sample shows how those three steps translate into code when configuring an IconButton:


val gestureConfig = rememberOneHandedGestureConfiguration(action = OneHandedGestureAction.Primary)
val indicatorState = remember { OneHandedGestureClickIndicatorState() }
val coroutineScope = rememberCoroutineScope()

OutlinedIconButton(
    onClick = onPlayPauseButtonClicked,
    modifier = Modifier.touchTargetAwareSize(IconButtonDefaults.LargeButtonSize)
        .oneHandedGesture(
            gestureConfiguration = gestureConfig,
            interactionSource = interactionSource,
            onGestureLabel = "play or pause",
            onGestureAvailable = { 
                coroutineScope.launch { indicatorState.showIndicator() } 
            },
            onGesture = onPlayPauseButtonClicked,
        ),
) {
    // button content goes here
    // See "Guided discovery with gesture indicators" section of this post for recommendations on adding a gesture indicator.
}

 

The GestureAction.Primary can also be used to scroll when the content is the end goal of the user journey, or there is a gesture actionable button off screen that the user can scroll to. Some examples include:

  • Scrolling through a notification to view the content and/or initiate a reply (available in TransformingLazyColumn and ScalingLazyColumn).
  • Paging through workout metrics or other content that doesn’t require the user to tap to continue the user journey (available in  HorizontalPager and VerticalPager).  

val scrollGestureConfig = rememberOneHandedGestureConfiguration(action = GestureAction.Primary)
val scrollIndicatorState = remember { OneHandedGestureScrollIndicatorState() }
val coroutineScope = rememberCoroutineScope()

TransformingLazyColumn(
    state = scrollState,
    contentPadding = contentPadding,
    modifier = Modifier
        .fillMaxSize()
        .oneHandedGesture(
            gestureConfiguration = scrollGestureConfig,
            onGestureLabel = "scroll",
            onGestureAvailable = { 
                coroutineScope.launch { scrollIndicatorState.showIndicator() } 
            },
            onGesture = { OneHandedGestureDefaults.scrollDown(scrollState) }
        )
) {
    // list content goes here
    // See "Guided discovery with gesture indicators" section of this post for recommendations on adding a gesture indicator.
}

Guided discovery with gesture indicators

To help users learn which gestures are available, gesture indicators work as hints to help discovery about which gestures are available on a screen.

These hints provide animated cues that inform users where they can perform a gesture. The framework manages the cadence and appearance of these hints, ensuring that they are helpful without being intrusive. System settings let users change the cadence to something less frequent if desired.

To integrate with hints, the API provides the following gesture indicator components:

The following example shows how to use the OneHandedGestureClickIndicator for a Button. See another example for using the OneHandedGestureScrollIndicator in our guidance.


val gestureConfig = rememberOneHandedGestureConfiguration(action = GestureAction.Primary)
val indicatorState = remember { OneHandedGestureClickIndicatorState() }
val coroutineScope = rememberCoroutineScope()

OutlinedIconButton(
    onClick = onPlayPauseButtonClicked,
    modifier = Modifier.touchTargetAwareSize(IconButtonDefaults.LargeButtonSize)
        .oneHandedGesture(
            gestureConfiguration = gestureConfig,
            interactionSource = interactionSource,
            onGestureLabel = "play or pause",
            onGestureAvailable = { 
                coroutineScope.launch { indicatorState.showIndicator() } 
            },
            onGesture = onPlayPauseButtonClicked,
        ),
) {
    OneHandedGestureClickIndicator(
        gestureConfiguration = gestureConfig,
        indicatorState = indicatorState,
    ) {
        val icon = if (playerUiModel.playbackState.isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow
        Icon(icon, contentDescription = "Play or Pause")
    }
}

Sample app showing gesture hint for media controls

We are already seeing early adoption of these APIs from partners like Spotify, who are using one-handed gestures to make music control more seamless on the go. By adopting the Modifier.oneHandedGesture into their Wear OS app, Spotify allows users to play or pause their music with the primary gesture action, which on Pixel Watch devices is the double-pinch gesture. This action triggers the same behavior as the physical play/pause button, and the user doesn’t  need to touch the screen.

Spotify app with gesture integration

Bring one-handed gestures to your app

You can begin experimenting with one-handed gestures today in the 1.7 beta release of Compose for Wear OS.

Ensure your app is running on Wear OS 7, which provides the underlying platform support for gesture detection. Check out our new one-handed gestures developer guide  to see how you can start building more convenient experiences for your users.

What’s new in the Jetpack Compose August ’26 release

Posted by Nick Butcher, Product Manager, Jetpack Compose


Today, the Jetpack Compose August ‘26 release is stable! This release brings version 1.12 across core Compose modules (see the full BOM mapping), introducing rich visual APIs like Mesh Gradients and Wide Color Gamut (WCG) support, structural layout features like named areas in Grid, seamless integration with Android’s Credential Manager, and significant testing and performance improvements.

To update your project to today’s release, upgrade your Compose BOM version to 2026.08.00:

implementation(platform("androidx.compose:compose-bom:2026.08.00"))

Breaking Changes

AGP & Compile SDK: Compose 1.12 updates compileSdk to API 37, requiring a minimum AGP 9.2.0. As a reminder, Compose will always target the latest compileSdk. Learn more about this change here.

Modifier.onFirstVisible() is deprecated: Migrate to Modifier.onVisibilityChanged(), which provides more precise visibility threshold tracking.

Graphics

Mesh Gradients

Compose 1.12 introduces MeshGradientPainter to help you create multi-point, organic color gradients.



val rows = 1
val columns = 1

val gradientPainter = remember {
    MeshGradientPainter(rows, columns) {
        // Parameters: row, column, position, color
        setVertex(0, 0, Offset(0f, 0f), Color.Red)     // Top-Left
        setVertex(0, 1, Offset(1f, 0f), Color.Blue)    // Top-Right
        setVertex(1, 0, Offset(0f, 1f), Color.Green)   // Bottom-Left
        setVertex(1, 1, Offset(1f, 1f), Color.Yellow)  // Bottom-Right
    }
}

Box(
    modifier = modifier
        .aspectRatio(16/9f)
        .fillMaxWidth()
        .paint(gradientPainter)
)

For more information and examples, see the documentation.

Wide Color Gamut & HDR Support

Modern displays offer extended color fidelity and higher dynamic range. In Compose 1.12, full pipeline support for Wide Color Gamut (P3) and HDR rendering has been enabled across Compose graphics, paint, and shaders. Colors defined in non-sRGB color spaces (such as Display P3) are preserved through to platform rendering without color clamping. Colors will safely fall back to sRGB if they use an unsupported color space (e.g. CieXyz, CieLab, or Oklab), rely on a color space on an unsupported Android version (e.g Bt2020Hlg on Android 13 and below), or if the app is running on Android 9 (API 28) and below.

Other notable changes:

  • LayerOutsets was added to GraphicsLayer & Modifier.graphicsLayer, which you can use to increase the visual bounds of the layer beyond its measured size. Apply LayerOutsets to avoid the implicit clipToBounds behavior when the layer is promoted to an offscreen buffer.

Styles

At Google I/O, we shared our early vision for the Compose Styles API—a unified, performant way to style components. Since then, we have continued building the underlying architecture to guarantee strict type safety and predictable correctness, and to support building custom design systems.

To ensure we get this foundational layer correct, the API will remain experimental, and you can expect breaking changes.

Runtime Optimizations

Keyed SideEffect Overload

SideEffect now supports key arguments, which lets you fire one-shot side effects whenever specific keys change. This can lead to better performance compared to using a LaunchedEffect or DisposableEffect when you don’t need the coroutine or dispose block. SideEffect is up to 90% faster than LaunchedEffect and around 20% faster than DisposableEffect. Note that SideEffect runs its effect before DisposableEffect and LaunchedEffect, so use caution if migrating existing effects to this API, especially for LaunchedEffects that rely on being dispatched to start after the current frame is completed.

@Composable
fun AnalyticsTracker(userId: String, screenName: String) {
    SideEffect(key1 = userId, key2 = screenName) {
        analytics.logScreenView(userId, screenName)
    }
}

Animation

DeferredTargetAnimation has graduated out of experimental status.

Interactive Two-Stage Transitions

New composables: DeferredAnimatedContent and DeferredAnimatedVisibility allow creating delightful two-stage transitions, e.g. for predictive back gesture tracking.

Manual animation control: During a transition's deferred phase, animated properties (like scale or offset) can now be manually manipulated in real-time (e.g., tracking a swipe gesture).

Seamless handoff: Once the deferred phase ends, the transition engine takes over and performs a seamless handoff, including velocity transfer, to the automatic transition.

Shared element support: A new permitTransformDuringDeferredTransition flag in SharedContentConfig controls whether shared elements visually transform along with their parent containers during the deferred transition phase.

val state = remember { DeferredTransitionState(initialScreen) }
val transition = rememberDeferredTransition(state)

if (predictiveBackInProgress) {
    state.defer(targetScreen)
} else {
    state.animateTo(targetScreen)
}

transition.DeferredAnimatedContent(
    targetState = targetScreen,
    mutableTransformSpec = {
       MutableContentTransform {
           // Manually manipulate properties during the deferred phase
           initialContentTransform { scale = swipeProgress }
       }
    }
) { screen ->
    ScreenContent(screen)
}

Below are two demos of use cases where a gesture-driven animation is handed off to a triggered animation:



Text, Input & Platform Integrations

Editable Text Formatting

New APIs offer rich-text formatting for editable text in BasicTextField. You can now programmatically apply and manipulate inline character and paragraph formatting using SpanStyle and ParagraphStyle via the new addStyle() method inside a TextFieldBuffer scope (such as inside textFieldState.edit { ... } or an InputTransformation). Additionally, TextFieldBuffer provides getSpanStyles() and getParagraphStyles() APIs that return TrackedRange objects, allowing you to read, update, or remove applied styles. To complement formatting creation, TextFieldState now exposes a read-only textStyles property for querying active styles across ranges, while TextFieldBuffer provides originalTextStyles to inspect formatting state prior to an edit. Text formatting and custom annotations are persisted across configuration changes.

val state = rememberTextFieldState("Formatted text in Compose 1.12")

// Apply bold and color styles to a range of text
state.edit {
    addStyle(
        SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue),
        start = 0,
        end = 9
    )
}

// Query active styles from TextFieldState
val currentStyles = state.textStyles

Text Selection

A new SelectionState API provides programmatic control and observability over text selection within a SelectionContainer. Hoisting a SelectionState object via rememberSelectionState() and passing into SelectionContainer exposes selectedTexts as a reactive list of AnnotatedStrings and provides methods like selectAll(), clear(), select(TextRange), and extendSelectionByWord().

Additionally, use getSelectableTexts() to retrieve all selectable text items in layout order and select text across composables in the SelectionContainer using a global range.

@Composable
fun ProgrammaticSelectionExample() {
    val selectionState = rememberSelectionState()

    Column {
        Button(
            onClick = { selectionState.selectAll() },
            modifier = Modifier.disableSelectionClearOnTap()
        ) {
            Text("Select All")
        }

        SelectionContainer(state = selectionState) {
            Text("Text content to be selected programmatically.")
        }
    }
}

Credential Manager Integration

Compose text fields now natively integrate with Android’s Credential Manager (API 34+) via the Autofill framework (below API 34 is handled by androidx.credentialslibrary). By attaching the new credentialRequest semantics property with CredentialRequestData, text inputs can prompt passkeys, saved credentials, or sign-in requests directly within the user input flow.

@Composable
fun LoginField(textFieldState: TextFieldState) {
    val credentialData = remember {
        CredentialRequestData(
            // Specify Credential Manager request options
        )
    }

    BasicTextField(
        state = textFieldState,
        modifier = Modifier.semantics {
            credentialRequest = credentialData
        }
    )
}

Other notable changes:

  • Support for font variation settings in downloadable fonts.
  • Enabled auto-scrolling when dragging text selection beyond the viewport in SelectionContainer.
  • Added support for automatic interaction sounds (clicks and focus navigation) to Compose components, with a new SoundEffectOnInteraction composable to allow opt-out. Note that as a consequence of this change, semantics click listeners must now be called from the main thread, which may affect a small number of test cases.
  • KeyboardType now includes Date, Time, DateTime, and SignedDecimal.
  • BasicSecureTextField now uses TextObfuscationMode.System by default, while RevealLastTyped serves as an absolute override.

Layout Enhancements

Named Areas in Grid Layout

Building complex 2D layouts is now easier with named areas in the @Experimental Grid component. Rather than managing numeric column and row indices across items, you can define semantic regions in your GridConfigurationScope and position composables by area name.

@OptIn(ExperimentalGridApi::class)
@Composable
fun DashboardLayout() {
    Grid(
        config = {
            area("header", row = 0, column = 0, rowSpan = 1, columnSpan = 2)
            area("sidebar", row = 1, column = 0)
            area("content", row = 1, column = 1)
            gap(16.dp)
        }
    ) {
        HeaderSection(modifier = Modifier.gridItem(areaId = "header"))
        NavigationSidebar(modifier = Modifier.gridItem(areaId = "sidebar"))
        MainContentView(modifier = Modifier.gridItem(areaId = "content"))
    }
}

Performance

As with every release, we continue to invest in Compose's performance to ensure that the framework helps you to build beautiful, performant apps. In this release we've focused on improving startup performance and are now seeing Time to Initial Display (the time it takes for an app to produce its first frame) that is comparable to Views in our benchmarks.

Testing & Tooling Upgrades

Test Synchronization

Compose 1.12 introduces new test APIs designed to reduce test execution times and eliminate flakiness during state sampling:

  • hasPendingWork: Passively checks if the UI has pending work without advancing the clock, which is ideal for manual animation loops.

  • runWithoutImplicitWait: Temporarily disables implicit synchronization when stepping through manual clock frames (e.g. animation tests).

  • @Test
    fun testAnimationStateFast() {
    
    composeTestRule.mainClock.autoAdvance = false
        
        while (composeTestRule.hasPendingWork()) {
            composeTestRule.mainClock.advanceTimeByFrame()
            composeTestRule.waitForIdle()
            
            composeTestRule.runOnUiThread {
                composeTestRule.runWithoutImplicitWait {
                    // This is most effective when querying multiple nodes in a single frame. 
                    // It prevents the redundant synchronization overhead that would 
                    // otherwise occur on every individual query.
                    val box1 = composeTestRule.onNodeWithTag("Box1").fetchSemanticsNode()
                    val box2 = composeTestRule.onNodeWithTag("Box2").fetchSemanticsNode()
                    
                    assertThat(box1.boundsInRoot.right).isAtMost(box2.boundsInRoot.left)
                }
            }
        }
    }

    Other notable changes:

    • The captureToImage API now allows you to capture a popup or dialog together with its anchor in a single bitmap.
    • Added onRootWithViewInteraction to scope Compose semantic searches to specific Android Views. This simplifies testing hybrid UIs, such as RecyclerViews, without requiring unique test tags in production code.
    • @PreviewWrapper annotations can now be applied to custom @MultiPreview classes, enabling reusable preview setups (such as custom themes) across multiple components.

    Happy Composing!

    As always, we value your input, so please share your feedback on these changes or what you'd like to see next on our issue tracker. Happy composing!

    Query report data with the Campaign Manager 360 API

    You can now use the reportData.query endpoint in the Campaign Manager 360 API to synchronously query your campaign performance data and retrieve structured JSON data directly in the response.

    The new reportData.query endpoint offers a simplified option for report data retrieval compared to the standard reporting API workflow:

    • Queries run synchronously with a 60-second execution limit, making this endpoint ideal for real-time dashboards and ad-hoc data explorations.
    • Report data is returned as structured JSON directly in the HTTP response, which eliminates file management overhead.
    • You no longer need to create a preconfigured Report resource to fetch data, as you can specify your dimensions, metrics, and filters directly in the request body.

    The existing Reports service is still recommended for retrieval of large datasets, scheduled jobs, or applications that rely on downloadable report files (CSV or Excel). This service requires multiple steps: developers must create and manage Report configurations, trigger a run of the report, poll for status updates, and download the report file.

    Get Started

    If you have any questions or need technical support, please reach out to Campaign Manager 360 API Support.