puzhibing
2026-08-21 5fa94b3718b4a78a4fcea4c97652b0ef20b7de3a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.xw.common
 
import android.view.Window
import android.view.WindowManager
import java.lang.ref.WeakReference
 
/**
 * 统一管理 APP 防截屏(FLAG_SECURE)开关。
 *
 * 默认行为:非 DEBUG 包禁止截屏,DEBUG 包允许截屏,保持与历史逻辑一致。
 *
 * 内部用弱引用集合保存所有受管理的 [Window](避免持有 Activity/Dialog 导致内存泄漏)。
 * 当 [allowScreenShot] 在 true/false 之间发生变化时,会自动刷新所有存活 window 的
 * FLAG_SECURE 标记,从而实时启用 / 禁用截屏。
 */
object SecureScreenShot {
 
    /** 受管理的 window 弱引用集合 */
    private val windowRefs = mutableListOf<WeakReference<Window>>()
 
    /**
     * 运行时是否允许截屏。默认 false(即开启防截屏)。
     * 赋值发生变化时,会自动刷新所有已登记 window 的截屏标记。
     */
    @Volatile
    var allowScreenShot: Boolean = false
        set(value) {
            if (field == value) return
            field = value
            refreshAll()
        }
 
    /**
     * 登记并立即为指定 [window] 应用当前的截屏配置。
     * 同一个 window 不会重复登记,失效的弱引用会被顺带清理。
     */
    @Synchronized
    fun apply(window: Window?) {
        window ?: return
        var exists = false
        val iterator = windowRefs.iterator()
        while (iterator.hasNext()) {
            val w = iterator.next().get()
            if (w == null) {
                iterator.remove()
            } else if (w === window) {
                exists = true
            }
        }
        if (!exists) {
            windowRefs.add(WeakReference(window))
        }
        applyFlag(window)
    }
 
    /**
     * 刷新所有存活 window 的截屏标记,并清理已失效的弱引用。
     */
    @Synchronized
    private fun refreshAll() {
        val iterator = windowRefs.iterator()
        while (iterator.hasNext()) {
            val w = iterator.next().get()
            if (w == null) {
                iterator.remove()
            } else {
                applyFlag(w)
            }
        }
    }
 
    /**
     * 根据当前配置为指定 [window] 应用或清除防截屏标记。
     *
     * - DEBUG 包或 [allowScreenShot] 为 true 时:清除 FLAG_SECURE,允许截屏
     * - 否则:添加 FLAG_SECURE,禁止截屏
     */
    private fun applyFlag(window: Window) {
        if (BuildConfig.DEBUG || allowScreenShot) {
            window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
        } else {
            window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)//禁止截屏
        }
    }
}