SecureWindowPolicy.kt

2.1 KB · raw

1package com.peachpasswords.ui.components23import android.view.Window4import android.view.WindowManager5import java.util.WeakHashMap67/**8 * The only owner of [WindowManager.LayoutParams.FLAG_SECURE] transitions.9 *10 * Release activities install a permanent policy and therefore no later11 * composable can accidentally clear the activity-wide protection. Debug-only12 * secret screens take reference-counted leases so nested effects cannot clear13 * the flag while another secret screen is still composed. Window instances are14 * weakly keyed because their lifecycle owns the actual resources.15 */16internal object SecureWindowPolicy {17    private data class State(18        var leases: Int = 0,19        var userSecure: Boolean = true,20        var userChoiceApplied: Boolean = false,21    )2223    private val states = WeakHashMap<Window, State>()2425    private fun evaluate(window: Window, state: State) {26        if (state.userSecure || (!state.userChoiceApplied && state.leases > 0)) {27            window.setFlags(28                WindowManager.LayoutParams.FLAG_SECURE,29                WindowManager.LayoutParams.FLAG_SECURE,30            )31        } else {32            window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)33        }34    }3536    /** Set the user's screenshot preference for this window. */37    fun setUserSecure(window: Window, secure: Boolean) {38        synchronized(states) {39            val state = states.getOrPut(window) { State() }40            state.userSecure = secure41            state.userChoiceApplied = true42            evaluate(window, state)43        }44    }4546    /** Acquire one debug/composable lease for a window. */47    fun acquire(window: Window) {48        synchronized(states) {49            val state = states.getOrPut(window) { State() }50            state.leases++51            evaluate(window, state)52        }53    }5455    /** Release one lease; re-evaluates the flag. */56    fun release(window: Window) {57        synchronized(states) {58            val state = states[window] ?: return59            if (state.leases > 0) state.leases--60            evaluate(window, state)61        }62    }63}