Securing Sensitive Information on Jetpack Compose’s AlertDialog: Preventing Screenshots
Mobile development often requires us to display sensitive information to users, such as coupons or rewards. And while this info may seem safe behind an alert dialog, a sneaky screenshot or screen recording can easily compromise it. In this article, we’ll explore potential solutions to prevent users from taking screenshots or screen recordings of sensitive information displayed on your mobile app.
But before we dive into the solution, let’s get real for a moment — no security measure is 100% foolproof. So while I’ll be showing you how to add an extra layer of security on Jetpack Compose’s AlertDialog, it’s important to understand that determined individuals can still find ways to bypass these restrictions, like taking a picture with a different device, for example.
Alright, now that we’ve got that out of the way, let’s move on to the good stuff! You’ve got two classes in your app — MainActivity.kt and CouponDialog.kt. MainActivity.kt is the main screen and it’s got a button that opens up CouponDialog.kt, a composable alert dialog displaying the juicy coupon info to your users. The problem? You want to make sure no one can capture that info through screenshots or recordings.
The MainActivity.kt code sets up the main screen:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ScreenshotPreventionTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(text = "Screenshot Prevention Demo", modifier = Modifier.padding(16.dp))
showCouponDialog()
}
}
}
}
}
@Composable
fun showCouponDialog() {
var showCouponDialog by remember { mutableStateOf(false) }
Button(
onClick = {
showCouponDialog = true
},
content = { Text("Show Coupon") }
)
if (showCouponDialog) {
CouponDialog {
showCouponDialog = false
}
}
}
}
The CouponDialog.kt code sets up the alert dialog:
@Composable
fun CouponDialog(onDismiss: () -> Unit) {
AlertDialog(
onDismissRequest = onDismiss,
text = {
Image(
painter = painterResource(id = R.drawable.qr_code),
contentDescription = "QR Code",
modifier = Modifier.padding(16.dp)
)
},
buttons = {
Button(
modifier = Modifier.fillMaxWidth().padding(16.dp),
onClick = onDismiss,
content = { Text("Close") }
)
},
properties = DialogProperties(
securePolicy = SecureFlagPolicy.SecureOn,
)
)
}
Here’s where the magic happens:
properties = DialogProperties(
securePolicy = SecureFlagPolicy.SecureOn,
)
By setting the securePolicy to SecureFlagPolicy.SecureOn, you’re locking down that dialog and preventing any screenshots or recordings. Try it for yourself — if someone tries to capture the info, they’ll either see a toast message saying “nope, not happening” or a black screen during recording.
Now, while this measure will definitely act as a wall, it’s important to remember that determined individuals may still find ways to bypass these restrictions. So while this is a step in the right direction, it’s not a guarantee of 100% security.
So go ahead, give yourself a pat on the back for adding an extra layer of protection and sit back knowing your sensitive info is a little bit safer. Happy coding!
Complete code available HERE.
The securePolicy property simply sets the FLAG_SECURE on the dialog’s window. For those familiar with the XML approach, this can also be achieved by setting the FLAG_SECURE on an Activity or DialogFragment. For reference, the code to do so is shown below:
// Activity:
activity?.window?.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
// DialogFragment
this.dialog?.window?.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)