Reputation: 45
I am currently trying to migrate my app to be compatible with Android 15. And as far as I concerned, in order to test Android 15 API, we need set up API 35 or VanillaIceCream API. Also based on the Android 15 migration, my app is set to edge-to-edge by default (which I don't want to).
After spending time to config the app, I manage to set the app to display correctly on my VanillaIceCream API emulator, make it display the status bar.
However, run the app in API 35 Emulator, I get this instead:
Basically the status bar items, such as time, WiFi, and battery indicator blend into the white background.
CompileSDK and targetSDK are all set to 35 in app's build.gradle.kts. And in my MainActivity.kt, i implemented the insets as follow:
My_App_Theme(
darkTheme = darkTheme
) {
Surface(
modifier = Modifier.windowInsetsPadding(
insets = WindowInsets.systemBars
),
color = MaterialTheme.colorScheme.background
) {
MY_APP(
darkTheme = darkTheme,
onThemeUpdated = { darkTheme = !darkTheme }
)
}
}
Is this the emulator error due to the fact that Android 15 hasn't been out yet, or there is something wrong of how I implemented the insets for the edge-to-edge configuration?
Upvotes: 4
Views: 2308
Reputation: 2565
Developers have to go edge to edge with Android 35 as @Randy pointed out correctly:
If your app must offer custom background protection to 3-button navigation or the status bar, your app should place a composable or view behind the system bar using WindowInsets.Type#tappableElement() to get the 3-button navigation bar height or WindowInsets.Type#statusBars.
However, i'd like to suggest an alternative solution: Material Design (especially the Scaffold) handles the hard parts automatically for you.
call enableEdgeToEdge()
in your onCreate()
. This way all api levels behave the same
Remove your Surface Wrapper:
Surface(
modifier = Modifier.windowInsetsPadding(
insets = WindowInsets.systemBars
),
color = MaterialTheme.colorScheme.background
)
Use a Material Scaffold
as your top level layout. Depending on your version (M2 or M3) you might need to specify a contentWindowInsets: WindowInsets parameter like contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
Report back if everything is working :)
Upvotes: 0
Reputation: 1
You can use <item name="android:fitsSystemWindows">true</item>
to ensure the content adjusts for the StatusBar and other system windows.
Upvotes: -1
Reputation: 1146
I feel like this is a terrible change, but according to the official documentation:
If your app must offer custom background protection to 3-button navigation or the status bar, your app should place a composable or view behind the system bar using WindowInsets.Type#tappableElement() to get the 3-button navigation bar height or WindowInsets.Type#statusBars.
https://developer.android.com/about/versions/15/behavior-changes-15
This makes it sound like you only need to do this if you need your app to interact with the status bar or navigation bar, but it's the only way I could find to color them.
There's probably an easier way but until someone else comes by with a better answer, here's what I did:
val statusBarColor = Color.Black
val navBarColor = Color.Black
// Set the icon/text colors in the status bar and nav bar
WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightStatusBars = !statusBarColor.isDark()
WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars = !navBarColor.isDark()
Row(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier.width(
WindowInsets.navigationBars.asPaddingValues().calculateStartPadding(LayoutDirection.Ltr)
).background(navBarColor).fillMaxHeight()
)
Column(modifier = Modifier.fillMaxSize().weight(1f, false),) {
Box(
modifier = Modifier.height(
WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
).background(statusBarColor).fillMaxWidth()
)
MainComposable(modifier = Modifier.fillMaxSize().weight(1f, false))
Box(
modifier = Modifier.height(
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
).background(navBarColor).fillMaxWidth()
)
}
Box(
modifier = Modifier.width(
WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(LayoutDirection.Ltr)
).background(navBarColor).fillMaxHeight()
)
}
In this code, MainComposable
is whatever you are using to display your app's main content. The modifier on it is necessary to make sure that the navigation bar coloring remains at the bottom of the screen when the navigation bar is down there.
This colors the status bar statusBarColor
and the navigation bar navBarColor
. Notice that it also accounts for the navigation bar rotating with the screen when the device is rotated. This also works with both the 3-button navigation, and gesture based navigation.
Also, my isDark
function looks like this:
/**
* Decides if a color is dark or light. Dark colors should have light (white) text displayed on them
* and vice versa.
*
* @return True if the color is dark, otherwise false
*/
fun Color.isDark() : Boolean {
// Extract individual colors. * 255 because they're returned as a fractional value
val r = red * 255
val g = green * 255
val b = blue * 255
// Code converted from here: (not the accepted answer)
// https://stackoverflow.com/questions/12043187/how-to-check-if-hex-color-is-too-black
val brightness = ((r * 0.2126) + (g * .7152) + (b * .0722)) * alpha
// https://learn.microsoft.com/en-us/answers/questions/1497116/how-to-find-if-a-given-color-is-dark-or-light
return brightness < 128
}
I also added enableEdgeToEdge()
in my activity's onCreate
function, so that it would be consistent across OS versions. (Without this, things will look different on API 35 vs older API levels, unless you adjust for that elsewhere.)
I also had to do some other adjustments, including not using the padding
variable in any Scaffold
objects. (That padding is the screen's padding for things like the status bar and navigation bar, which we are now accounting for ourselves.)
Upvotes: 0
Reputation: 415
You can change the color of the status bar icons using WindowInsetsControllerCompat((context as Activity).window, context.window.decorView).isAppearanceLightStatusBars
.
Example
LaunchedEffect(Unit) {
WindowInsetsControllerCompat((context as Activity).window, context.window.decorView).isAppearanceLightStatusBars = true //false for black
}
Upvotes: 0