Here’s how the UI of an app using edge-to-edge can go wrong because of a cutout:
Among other issues, this app’s cutout overlaps the list content.
This is why it is important to test how your UI reacts to different types of cutouts through your Compose Preview.
In the Preview picker, under the Hardware section, find the Cutout dropdown and specify the cutout you would like to test.
If you are doing this through code, note that you will first have to specify a device spec either through a specific device or a specific width and height in pixels.
@Preview(showSystemUi = true, device = "spec:parent=pixel_8")
@Preview(showSystemUi = true, device = "spec:width=1080px,height=2340px")
Then, within the device spec, you can also add cutout and set it equal to the type of cutout you want to test.
@Preview(showSystemUi = true, device =
“spec:width=1080px,height=2400px,cutout=punch_hole”)
Navigation bar
For edge-to-edge, there are different UI standards for gesture navigation vs three button navigation.
Gesture navigation:
- Transparent by default
- Bottom offset is disabled, but you can apply insets
setNavigationBarColor
is disabled
Three button navigation:
- Opacity set to 80% by default
- Bottom offset is disabled but you can apply insets
- Color is the window background by default
The navigation bar in showSystemUi
will show you the gesture navigation bar by default, but to test both types of navigation bars in your Preview, you can specify navigation using the Preview picker or through the device parameter.
To specify through the Preview picker, in the Hardware section, find the Navigation dropdown and set your Preview’s navigation bar.
If you are using code to test the navigation bars, you will need to specify a specific device or a specific width and height in pixels.
@Preview(showSystemUi = true, device = "spec:parent=pixel_8")
Then, you can add navigation and set it to buttons for three button navigation or gesture for gesture navigation.
@Preview(showSystemUi = true, device =
"spec:parent=pixel_8,navigation=buttons")
Now that you know how to test your app’s UI edge-to-edge implementation through Compose Preview, let’s go over automated testing.
Once you have manually tested that your screen handles edge-to-edge as expected, you should consider adding automated tests to catch future regressions.
We recommend using screenshot tests to verify your edge-to-edge implementation, as they verify the placement and dimension of your insets and the content that might be drawn behind.
You can use instrumented tests for the highest fidelity on emulators or physical devices. A single foldable emulator can cover most cases, and you can use Espresso Device to set the different screen orientations and foldable state:
To switch between navigation modes, you can use UI Automator to pass adb
commands:
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
executeShellCommand(
"cmd overlay enable-exclusive " +
"com.android.internal.systemui.navbar.gestural", // or .threebutton
)
}
The system bar contains a clock and icons that change every minute, breaking screenshot tests. You can enable demo mode via adb commands to show the same system bar content every time:
Note that adb
commands are not synchronized, so you might need to implement a mechanism to wait or retry until each test passes. Alternatively, if you use Compose, there is a new wrapper that can help you catch most regressions:
Compose 1.8.0-alpha01 ui-test includes a new DeviceConfigurationOverride
for testing window insets, called DeviceConfigurationOverride.WindowInsets
.
This allows for specifying an arbitrary WindowInsetsCompat
to apply to the composable under test:
composeTestRule.setContent {
DeviceConfigurationOverride(
DeviceConfigurationOverride.WindowInsets(
WindowInsetsCompat.Builder()
.setInsets(
WindowInsetsCompat.Type.statusBars(),
DpRect(
left = 0.dp,
top = 64.dp,
right = 0.dp,
bottom = 0.dp,
).toAndroidXInsets(),
)
.setInsets(
WindowInsetsCompat.Type.navigationBars(),
DpRect(
left = 64.dp,
top = 0.dp,
right = 64.dp,
bottom = 64.dp,
).toInsets(),
)
.build(),
),
)
) {
Box {
content() // Your content under test
DebugVisibleWindowInsets(Modifier.fillMaxSize()) // Debug overlay (optional)
}
}
}
This can then be combined with a debug overlay for showing where the insets are:
@Composable
fun DebugVisibleWindowInsets(
modifier: Modifier = Modifier,
debugColor: Color = Color.Magenta.copy(alpha = 0.5f),
) {
Box(modifier = modifier.fillMaxSize()) {
Spacer(
modifier = Modifier
.align(Alignment.CenterStart)
.fillMaxHeight()
.windowInsetsStartWidth(WindowInsets.safeDrawing)
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Vertical))
.background(debugColor),
)
Spacer(
modifier = Modifier
.align(Alignment.CenterEnd)
.fillMaxHeight()
.windowInsetsEndWidth(WindowInsets.safeDrawing)
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Vertical))
.background(debugColor),
)
Spacer(
modifier = Modifier
.align(Alignment.TopCenter)
.fillMaxWidth()
.windowInsetsTopHeight(WindowInsets.safeDrawing)
.background(debugColor),
)
Spacer(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.windowInsetsBottomHeight(WindowInsets.safeDrawing)
.background(debugColor),
)
}
}
Putting both together, a screenshot test can visually show where the insets are, and reveal if there is content that would be obscured by the insets, like the snackbar is below:
For an example of this in action, check out this Now in Android PR, which adds screenshot tests with applying insets: https://github.com/android/nowinandroid/pull/1498/
Your app might decide to create a QA team to test every screen, or at least your most important screens.
There are three approaches to assist your QA team in seeing the impacts of the edge-to-edge enforcement:
- Distribute APKs that are targeting SDK 35 on Android 15 devices or emulators.
- OR, enable the
ENFORCE_EDGE_TO_EDGE
flag in the App Compatibility Change Developer Option on an Android 15 device without having to target SDK 35. - OR, call
enableEdgeToEdge
on each Activity to simulate the Android 15 platform enforcement without having to target SDK 35 and without needing an Android 15 device.
Apps targeting API35 will be edge-to-edge by default in order to give your users a more satisfying and high quality experience. You can test your UI, including cutouts and navigation bars, using Compose Preview in the Canary version of Android Studio Ladybug, and in automating testing with the new DeviceConfigurationOverride
. Please be sure to leave us any feedback using these instructions.