For the complete documentation index, see llms.txt. This page is also available as Markdown.

secure pin entry snippet

A PIN entry screen is a high-value target: attackers try to read it via screen capture, steal it through tap-jacking overlays, or scrape it with a malicious accessibility service. Native Android gives you several settings that harden the screen itself, and these pair naturally with runtime protections. Before trusting any on-screen input, RASP+ features such as Overlay Detection, Accessibility Misuse Detection, and Malware Detection should be running as a general prerequisite — they cover the device- and app-level threats that no per-screen flag can see. The snippets below focus on the Android-native hardening you apply to the screen itself.

The key measures are: FLAG_SECURE to block screenshots, screen recording, and exposure in the recents thumbnail; obscured-touch filtering to defeat tap-jacking overlays; and input-field settings that keep the PIN out of autofill, keyboard personalized-learning, and the clipboard.

@Composable
fun SecurePinEntry() {
    val view = LocalView.current

    // FLAG_SECURE: no screenshots, no screen recording, blanked in recents.
    DisposableEffect(Unit) {
        val window = (view.context as Activity).window
        window.setFlags(
            WindowManager.LayoutParams.FLAG_SECURE,
            WindowManager.LayoutParams.FLAG_SECURE
        )
        onDispose { window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) }
    }

    var pin by remember { mutableStateOf("") }

    OutlinedTextField(
        value = pin,
        onValueChange = { if (it.length <= 6 && it.all(Char::isDigit)) pin = it },
        label = { Text("PIN") },
        singleLine = true,
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(
            // Password keyboard type is treated as sensitive: the IME keeps
            // entered digits out of personalized-learning/suggestions.
            keyboardType = KeyboardType.NumberPassword,
            imeAction = ImeAction.Done
        ),
        // pointerInteropFilter requires @OptIn(ExperimentalComposeUiApi::class).
        // Reject taps that arrive while another window sits on top (tap-jacking).
        modifier = Modifier.pointerInteropFilter { event ->
            val obscured = event.flags and
                (MotionEvent.FLAG_WINDOW_IS_OBSCURED or
                    MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED)
            obscured != 0 // true = consume & ignore the event
        }
    )
}

In Compose, set importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO on the host AndroidView/root, or disable autofill for the Activity, since the TextField itself does not expose an autofill flag.

Layout (res/layout/activity_pin.xml):

Activity:

filterTouchesWhenObscured and FLAG_SECURE defend the screen locally, but they cannot detect a malicious accessibility service or malware already resident on the device. Keep Talsec Accessibility Misuse Detection, Overlay Detection, and Malware Detection enabled app-wide so those threats are caught before the user ever reaches this screen.

Was this helpful?