👨🏼‍💻 Lifecycle-aware in Jetpack Compose: Disabling Screenshot for a Single Screen

Ahmet Furkan Sevim
Huawei Developers
Published in
4 min readFeb 2, 2024
Jetpack Compose Lifecycle

Introduction

While developing a new project, we found the need to disable the screenshot functionality for some screens, particularly for security reasons. Like many other companies in app development, we choose to utilize Jetpack Compose.

However, we couldn’t disable screen shot for a single composable screen. By default, @Composable functions in Jetpack Compose are not lifecycle-aware. To observe events such as when Composables are displayed on the screen or removed from the screen, it's necessary to write an additional function.

In this article, I’ll explain methods to make our Composables lifecycle-aware and demonstrate how to prevent taking screenshots for a specific screen. Enjoy the read!

1. Lifecycle-aware Components

Before diving into code, let’s explore the concept of a “lifecycle-aware component.” In the Android Framework, most app components come with attached lifecycles, which are managed by the operating system or framework code within your application’s process. These lifecycles are fundamental to how Android operates, and it is crucial for your application to adhere to them. Failure to do so can lead to memory leaks or even application crashes.

A “Lifecycle” is a class that holds information about the lifecycle state of a component, such as an activity or a fragment. It allows other objects to observe this state. Lifecycle uses two main enumerations to track the lifecycle status for its associated component:

  • Event: These are lifecycle events dispatched from the framework and the Lifecycle class, corresponding to the callback events in activities and fragments.
  • State: This represents the current state of the component tracked by the Lifecycle object.

2. Composable Lifecycle

I am writing a helper function to make a composable function lifecycle-aware, allowing you to observe lifecycle events of a given LifecycleOwner within the Compose hierarchy.

@Composable
fun ComposableLifeCycle(
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
onEvent: (LifecycleOwner, Lifecycle.Event) -> Unit
) {
// If `lifecycleOwner` changes, dispose and reset the effect
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { source, event ->
onEvent(source, event)
}
lifecycleOwner.lifecycle.addObserver(observer)

onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}
}

lifecycleOwner: Represents the owner of the lifecycle (e.g., an Activity or Fragment). It has a default value of LocalLifecycleOwner.current, which refers to the current lifecycle owner in the Compose hierarchy.

onEvent: A lambda function that takes two parameters - LifecycleOwner and Lifecycle.Event. This lambda will be invoked when a lifecycle event occurs.

3. How to Use: Disabling Screenshots for a Single Screen

Now, let’s explore how to utilize the helper function we defined in the previous section. As mentioned in the introduction, I’ll demonstrate how to disable screenshot capture for a specific screen using this function.

@Composable
fun ProtectedScreen(modifier: Modifier = Modifier) {

val activity = LocalContext.current.findActivity()

ComposableLifeCycle { source, event ->
when (event) {
Lifecycle.Event.ON_START-> {
activity.window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE
)
}

Lifecycle.Event.ON_PAUSE -> {
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}

else -> {}
}
}

Text(text = "Screen B")
}

val activity = LocalContext.current.findActivity(): This line uses the LocalContext to obtain the current activity associated with the Compose context.

ComposableLifeCycle { source, event -> ... }: This is a usage of the previously explained ComposableLifeCycle composable function. It observes the lifecycle events of the ProtectedScreen composable. Inside the lambda, it checks for two specific lifecycle events:

  • Lifecycle.Event.ON_START: When the composable is started (i.e., becomes visible), it sets a flag on the window to disable screenshots using activity.window.setFlags(...).
  • Lifecycle.Event.ON_PAUSE: When the composable is paused (e.g., when another activity comes to the foreground), it clears the flag to allow screenshots using activity.window.clearFlags(...).
  • The else -> {} block handles other lifecycle events, but in this case, it is empty.

4. Result

A screen recording has been captured to showcase the functionality. When attempting to capture a screenshot on the protected screen, the video turns black.

Result

Conclusion

In conclusion, by incorporating lifecycle awareness with the ComposableLifeCycle function, we successfully disabled screenshot functionality for a single screen in a Jetpack Compose project.

You can find the full code here.

References

--

--