Let There Be Bright (Screens)!

Barry Irvine
Go City Engineering
3 min readMay 3, 2022
Light bulb

A common feature of apps that show QR codes is to increase the brightness to full whenever you open the ticket page. This makes it easier for QR code scanners to read them without the user having to manually adjust the brightness of their device.

Here at Go City, rather than shoehorning this year’s app relaunch into the single module, 100% Java codebase that we inherited, we’re busy rewriting it from the ground-up with all the modern Android Architecture Components, Kotlin, Hilt and Jetpack Compose. So although we knew how to do this in the old view-based system, what’s the best way to do it with Compose?

It was extremely simple:DisposableEffect is your friend!

The first problem we need to solve is how to access the Activity, since there is no LocalActivity.current in Jetpack Compose. I solved this by creating a very simple extension method to get the Activity from the current Context. Casting the Context directly to Activity is not very safe — if your Composable sits inside a dialog for example, you’ll find that the Activity is wrapped by two ContextWrappers!

fun Context.getActivity(): Activity? {
var context = this
while (context is ContextWrapper) {
if (context is Activity) {
return context
}
context = context.baseContext
}
return null
}

This simple extension just loops through all the context wrappers until it finds an Activity (unless you passed in an Application Context or something whereby there is no Activity, so null would be returned).

Now that our extension method is out of the way we can create our ForceBrightness Composable. We want to be able to add this to our Composable screens in such a way that when we exit that Screen we revert to the user’s original brightness.

We can get original brightness thusly: activity.window.attributes.screenBrightness. Brightness is simply a float between 0f and 1f, where 1f is 100% brightness. To update the brightness we simply take the current window attributes, modify the brightness to 1f and reassign the attributes to the window. As discussed, once the Composable goes out of scope (i.e. it’s disposed) we want to revert back to the initial brightness.

Putting all this together we get this simple Composable that we can add to any screen where we want to override the brightness. It defaults to 100% brightness but this too can be adjusted by passing in a different value.

Et voila!:

/**
* Forces a Compose screen to be at a given brightness level and reverts after exiting screen
*/
@Composable
fun ForceBrightness(brightness: Float = 1f) {
val activity = requireNotNull(LocalContext.current.getActivity())
DisposableEffect(Unit) {
val attributes = activity.window.attributes
val originalBrightness = attributes.screenBrightness
activity.window.attributes = attributes.apply { screenBrightness = brightness }
onDispose {
activity.window.attributes = attributes.apply { screenBrightness = originalBrightness }
}
}
}

Using it couldn’t be simpler:

@Composable
fun MyScreen() {
ForceBrightness()
Text("This screen is now at full brightness regardless of how dim you've set it on your device") {
}

As usual, if you enjoyed this post, I’d appreciate some claps, a follow, share or tweet and if you have any questions please don’t hesitate to post them below.

--

--

Barry Irvine
Go City Engineering

Writing elegant Android code is my passion — but with 20+ years experience in roles from programme delivery to working at the coal face, I’ve seen it all.