A Jetpack Compose implementation of a privacySensitive() modifier to protect sensitive UI content, inspired by SwiftUI's privacy-sensitive functionality.
PrivacySensitive provides a declarative way to mark UI components as sensitive, automatically hiding them when screenshots are taken, screen recording is active, or when the app is backgrounded. This helps protect user privacy by preventing sensitive information like passwords, personal data, or financial information from being captured unintentionally.
- Screenshot Protection: Automatically hides sensitive content when screenshots are taken
- Screen Recording Protection: Detects and responds to screen recording sessions
- App State Awareness: Hides content when app moves to background
- Easy Integration: Simple modifier-based API that follows Jetpack Compose patterns
- SwiftUI Inspired: Familiar API for developers coming from iOS development
- Customizable: Flexible configuration options for different privacy levels
Add the dependency to your build.gradle.kts file:
dependencies {
implementation "com.github.remziakgoz:PrivacySensitive:1.0.0"
}@Composable
fun LoginScreen() {
Column {
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.privacySensitive() // Protects password field
)
Text(
text = "Account Balance: $5,432.10",
modifier = Modifier.privacySensitive() // Protects financial info
)
}
}// Basic privacy protection (default)
Text("Sensitive Info").privacySensitive()
// High sensitivity - more aggressive protection
Text("Very Sensitive Info").privacySensitive(
level = PrivacyLevel.HIGH
)
// Custom configuration
Text("Custom Protected").privacySensitive(
level = PrivacyLevel.MEDIUM,
hideOnScreenshot = true,
hideOnRecording = true,
hideOnBackground = false
)| Parameter | Type | Default | Description |
|---|---|---|---|
level |
PrivacyLevel |
STANDARD |
Overall sensitivity level |
hideOnScreenshot |
Boolean |
true |
Hide when screenshot is detected |
hideOnRecording |
Boolean |
true |
Hide during screen recording |
hideOnBackground |
Boolean |
true |
Hide when app is backgrounded |
replacementContent |
@Composable |
null |
Custom content to show when hidden |
TextField(
value = creditCard,
onValueChange = { creditCard = it },
modifier = Modifier.privacySensitive(
replacementContent = {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.background(Color.Gray.copy(alpha = 0.3f)),
contentAlignment = Alignment.Center
) {
Text("β’β’β’β’β’β’β’β’β’β’β’β’β’β’β’β’")
}
}
)
)- Screenshot Detection: Monitors system events and content observer callbacks
- Screen Recording Detection: Uses accessibility services and system APIs to detect recording
- App Lifecycle: Observes app state changes (foreground/background)
- Content Replacement: Dynamically replaces sensitive content with placeholder or custom views
PrivacyLevel.LOW: Basic protection, minimal performance impactPrivacyLevel.STANDARD: Balanced protection suitable for most use casesPrivacyLevel.HIGH: Maximum protection with more aggressive detectionPrivacyLevel.CUSTOM: Full control over individual protection features
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
PrivacySensitive.configure {
defaultLevel = PrivacyLevel.HIGH
enableDebugLogging = BuildConfig.DEBUG
customDetectionInterval = 100L // milliseconds
}
}
}@Composable
fun ConditionallyProtectedContent() {
val isLoggedIn by viewModel.isLoggedIn.collectAsState()
Text(
text = userBalance,
modifier = Modifier.privacySensitive(
enabled = isLoggedIn // Only protect when user is logged in
)
)
}- Minimum SDK: 21 (Android 5.0)
- Target SDK: 34 (Android 14)
- Compile SDK: 34
The library includes test utilities for verifying privacy protection behavior:
@Test
fun testPrivacySensitiveModifier() {
composeTestRule.setContent {
Text(
text = "Sensitive",
modifier = Modifier.privacySensitive()
)
}
// Simulate screenshot
PrivacySensitiveTestUtils.simulateScreenshot()
// Verify content is hidden
composeTestRule.onNodeWithText("Sensitive").assertDoesNotExist()
}Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Clone the repository
- Open in Android Studio
- Sync Gradle files
- Run tests:
./gradlew test
MIT License
Copyright (c) 2024 Remzi AkgΓΆz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- Inspired by SwiftUI's privacy-sensitive modifier
- Thanks to the Jetpack Compose team for the excellent UI framework
- Community contributors and testers
- SwiftUI Privacy Sensitive Modifier
- Android FLAG_SECURE Documentation
- Jetpack Compose Custom Modifiers
Made with β€οΈ by Remzi AkgΓΆz
For questions or support, please open an issue on GitHub.