
Lightweight zero-overhead screen-capture prevention and detection, blocking screenshots and recordings, auto-obscuring task-switcher previews, real-time capture/screenshot events, declarative and imperative APIs.
Multiplatform screen-capture prevention and detection for Kotlin Multiplatform, Android, and iOS.
ComposeShield is a lightweight, zero-overhead screen-capture protection library for Kotlin Multiplatform (Android & iOS). It blocks screenshots and screen recordings, detects active capture sessions, and automatically obscures app previews in the OS task switcher / recents menu.
Whether you need to shield an entire application with a single line of code, guard individual sensitive screens, or manage protection imperatively from ViewModels, traditional Views, or Swift/UIKit, ComposeShield delivers a unified, lifecycle-safe API backed by each operating system's strongest native security mechanisms.
SecureContent {} to automatically acquire and release protection with Compose lifecycle—never leak protection or forget teardown.StateFlow streams for live screen recording, mirroring, and external display detection.Dialog, Popup, and ModalBottomSheet automatically inherit protection without boilerplate.Protecting every screen in your application requires just a single root wrapper:
@Composable
fun App() {
// Secures all screens, dialogs, popups, and recents switcher thumbnails
SecureContent {
AppContent()
}
}Or initialize app-wide protection imperatively at startup:
// Android (Application.onCreate), iOS (AppDelegate / SwiftUI App.init), or Common setup:
ComposeShield.protect()Add the dependency to your project's build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.abdo-essam:composeshield:0.1.0")
}
}
}dependencies {
implementation("io.github.abdo-essam:composeshield:0.1.0")
}SecureContent is the recommended approach for Jetpack Compose and Compose Multiplatform. Protection is acquired when entering composition and automatically released when disposed.
@Composable
fun App() {
SecureContent {
AppNavHost() // Secures the entire window hierarchy
}
}@Composable
fun SensitiveScreen() {
SecureContent {
SensitiveContent()
}
}Window Scoping Note: Mobile operating systems apply hardware-level capture protection at the window level. When
SecureContentis active, the entire window hosting the composable is protected against screenshots and recordings.
In Jetpack Compose and Compose Multiplatform, child surfaces like Dialog, Popup, and Material3 ModalBottomSheet create separate platform windows.
ComposeShield automatically attaches protection to any child window created inside a SecureContent block—no extra wrappers required:
SecureContent {
Column {
SensitiveContent()
if (showSheet) {
// ModalBottomSheet creates its own window — automatically secured!
ModalBottomSheet(onDismissRequest = { showSheet = false }) {
BottomSheetContent()
}
}
}
}For non-composable architectures, Native Android (XML), Native iOS (Swift), or business logic controllers, use the imperative API:
class SensitiveActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sensitive)
ComposeShield.protect() // Works with standard Android Views
}
}class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ComposeShield.protect()
}
}import ComposeShield
// In your AppDelegate, SceneDelegate, or UIViewController
ComposeShield.shared.protect(capabilities: nil)
// To release protection
ComposeShield.shared.unprotect(capabilities: nil)class SensitiveViewModel : ViewModel() {
private val protectionHandle = ComposeShield.protect()
override fun onCleared() {
protectionHandle.unprotect()
}
}
// Or execute a block with automatic RAII scoping:
suspend fun executeSensitiveOperation(): Result = ComposeShield.protect().use {
api.fetchConfidentialData()
}Observe live screen recording, mirroring, and external display connections reactively:
@Composable
fun SecurityMonitor() {
val captureState by ComposeShield.captureState.collectAsState()
if (captureState == CaptureState.Active) {
RecordingWarningBanner()
}
}Important:
CaptureState.Inactivesignifies "no active capture detected", not an absolute guarantee. Always rely onSecureContent/protect()for prevention rather than gating sensitive UI on detection alone.
Listen for post-hoc screenshot events (e.g. to log security audit events or show advisory notices):
LaunchedEffect(Unit) {
ComposeShield.screenshotEvents.collect {
analytics.logSecurityEvent("screenshot_captured")
}
}(Note: On Android, screenshot detection callbacks are precluded by the OS while FLAG_SECURE prevention is active.)
If an underlying OS mechanism is unavailable or fails at runtime, ComposeShield ensures the app remains stable and notifies your diagnostic handlers:
SecureContent(
onProtectionFailure = { failedCapability ->
logger.warn("Protection mechanism unavailable for $failedCapability")
}
) {
SensitiveContent()
}ComposeShield uses a unified, reference-counted state engine. Imperative claims (ComposeShield.protect()) and declarative boundaries (SecureContent {}) interoperate seamlessly without race conditions:
| Approach | Context | Lifetime | Teardown |
|---|---|---|---|
SecureContent {} |
Jetpack Compose / Compose Multiplatform | Tied to composition | Automatic on disposal |
ComposeShield.protect() |
Application, Activity, Swift, ViewModel | Explicit handle |
handle.unprotect() or use {}
|
| Capability | Android | iOS | Underlying Native Mechanism |
|---|---|---|---|
| Screenshot Prevention | ✅ API 24+ | ✅ iOS 15+ |
Android: FLAG_SECUREiOS: Secure layer reparenting |
| Recording Prevention | ✅ API 24+ | ✅ iOS 15+ |
Android: FLAG_SECUREiOS: Secure layer reparenting |
| Capture Detection | ✅ API 35+ | ✅ iOS 15+ |
Android: ScreenRecordingCallbackiOS: UITraitSceneCaptureState
|
| Screenshot Events | ✅ API 34+† | ✅ iOS 15+ |
Android: ScreenCaptureCallbackiOS: userDidTakeScreenshotNotification
|
| Task Switcher Obscuring | ✅ API 33+ | ✅ iOS 15+ |
Android: setRecentsScreenshotEnablediOS: Scene transition overlay |
† On Android, screenshot event callbacks are intentionally suppressed by the OS while FLAG_SECURE is active.
For in-depth architecture and platform notes, see:
ComposeShield enforces the strongest official, review-compliant protections provided by Android and iOS. However, please note the physical and platform security boundaries:
If ComposeShield helps protect your application or team, please consider sponsoring the project on GitHub or starring the repository ⭐!
Please do not file public GitHub issues for security vulnerabilities. Report security findings directly to abdo-essam@hotmail.com or submit a confidential report via GitHub Private Vulnerability Reporting.
Copyright 2026 Abdo Essam
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Multiplatform screen-capture prevention and detection for Kotlin Multiplatform, Android, and iOS.
ComposeShield is a lightweight, zero-overhead screen-capture protection library for Kotlin Multiplatform (Android & iOS). It blocks screenshots and screen recordings, detects active capture sessions, and automatically obscures app previews in the OS task switcher / recents menu.
Whether you need to shield an entire application with a single line of code, guard individual sensitive screens, or manage protection imperatively from ViewModels, traditional Views, or Swift/UIKit, ComposeShield delivers a unified, lifecycle-safe API backed by each operating system's strongest native security mechanisms.
SecureContent {} to automatically acquire and release protection with Compose lifecycle—never leak protection or forget teardown.StateFlow streams for live screen recording, mirroring, and external display detection.Dialog, Popup, and ModalBottomSheet automatically inherit protection without boilerplate.Protecting every screen in your application requires just a single root wrapper:
@Composable
fun App() {
// Secures all screens, dialogs, popups, and recents switcher thumbnails
SecureContent {
AppContent()
}
}Or initialize app-wide protection imperatively at startup:
// Android (Application.onCreate), iOS (AppDelegate / SwiftUI App.init), or Common setup:
ComposeShield.protect()Add the dependency to your project's build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.abdo-essam:composeshield:0.1.0")
}
}
}dependencies {
implementation("io.github.abdo-essam:composeshield:0.1.0")
}SecureContent is the recommended approach for Jetpack Compose and Compose Multiplatform. Protection is acquired when entering composition and automatically released when disposed.
@Composable
fun App() {
SecureContent {
AppNavHost() // Secures the entire window hierarchy
}
}@Composable
fun SensitiveScreen() {
SecureContent {
SensitiveContent()
}
}Window Scoping Note: Mobile operating systems apply hardware-level capture protection at the window level. When
SecureContentis active, the entire window hosting the composable is protected against screenshots and recordings.
In Jetpack Compose and Compose Multiplatform, child surfaces like Dialog, Popup, and Material3 ModalBottomSheet create separate platform windows.
ComposeShield automatically attaches protection to any child window created inside a SecureContent block—no extra wrappers required:
SecureContent {
Column {
SensitiveContent()
if (showSheet) {
// ModalBottomSheet creates its own window — automatically secured!
ModalBottomSheet(onDismissRequest = { showSheet = false }) {
BottomSheetContent()
}
}
}
}For non-composable architectures, Native Android (XML), Native iOS (Swift), or business logic controllers, use the imperative API:
class SensitiveActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sensitive)
ComposeShield.protect() // Works with standard Android Views
}
}class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ComposeShield.protect()
}
}import ComposeShield
// In your AppDelegate, SceneDelegate, or UIViewController
ComposeShield.shared.protect(capabilities: nil)
// To release protection
ComposeShield.shared.unprotect(capabilities: nil)class SensitiveViewModel : ViewModel() {
private val protectionHandle = ComposeShield.protect()
override fun onCleared() {
protectionHandle.unprotect()
}
}
// Or execute a block with automatic RAII scoping:
suspend fun executeSensitiveOperation(): Result = ComposeShield.protect().use {
api.fetchConfidentialData()
}Observe live screen recording, mirroring, and external display connections reactively:
@Composable
fun SecurityMonitor() {
val captureState by ComposeShield.captureState.collectAsState()
if (captureState == CaptureState.Active) {
RecordingWarningBanner()
}
}Important:
CaptureState.Inactivesignifies "no active capture detected", not an absolute guarantee. Always rely onSecureContent/protect()for prevention rather than gating sensitive UI on detection alone.
Listen for post-hoc screenshot events (e.g. to log security audit events or show advisory notices):
LaunchedEffect(Unit) {
ComposeShield.screenshotEvents.collect {
analytics.logSecurityEvent("screenshot_captured")
}
}(Note: On Android, screenshot detection callbacks are precluded by the OS while FLAG_SECURE prevention is active.)
If an underlying OS mechanism is unavailable or fails at runtime, ComposeShield ensures the app remains stable and notifies your diagnostic handlers:
SecureContent(
onProtectionFailure = { failedCapability ->
logger.warn("Protection mechanism unavailable for $failedCapability")
}
) {
SensitiveContent()
}ComposeShield uses a unified, reference-counted state engine. Imperative claims (ComposeShield.protect()) and declarative boundaries (SecureContent {}) interoperate seamlessly without race conditions:
| Approach | Context | Lifetime | Teardown |
|---|---|---|---|
SecureContent {} |
Jetpack Compose / Compose Multiplatform | Tied to composition | Automatic on disposal |
ComposeShield.protect() |
Application, Activity, Swift, ViewModel | Explicit handle |
handle.unprotect() or use {}
|
| Capability | Android | iOS | Underlying Native Mechanism |
|---|---|---|---|
| Screenshot Prevention | ✅ API 24+ | ✅ iOS 15+ |
Android: FLAG_SECUREiOS: Secure layer reparenting |
| Recording Prevention | ✅ API 24+ | ✅ iOS 15+ |
Android: FLAG_SECUREiOS: Secure layer reparenting |
| Capture Detection | ✅ API 35+ | ✅ iOS 15+ |
Android: ScreenRecordingCallbackiOS: UITraitSceneCaptureState
|
| Screenshot Events | ✅ API 34+† | ✅ iOS 15+ |
Android: ScreenCaptureCallbackiOS: userDidTakeScreenshotNotification
|
| Task Switcher Obscuring | ✅ API 33+ | ✅ iOS 15+ |
Android: setRecentsScreenshotEnablediOS: Scene transition overlay |
† On Android, screenshot event callbacks are intentionally suppressed by the OS while FLAG_SECURE is active.
For in-depth architecture and platform notes, see:
ComposeShield enforces the strongest official, review-compliant protections provided by Android and iOS. However, please note the physical and platform security boundaries:
If ComposeShield helps protect your application or team, please consider sponsoring the project on GitHub or starring the repository ⭐!
Please do not file public GitHub issues for security vulnerabilities. Report security findings directly to abdo-essam@hotmail.com or submit a confidential report via GitHub Private Vulnerability Reporting.
Copyright 2026 Abdo Essam
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0