@@ -15,70 +15,48 @@ import com.aheaditec.talsec_security.security.api.Talsec
1515import io.flutter.Log
1616import java.util.function.Consumer
1717
18- internal class ScreenProtector : DefaultLifecycleObserver {
19- companion object {
20- private const val TAG = " ScreenProtector"
21- private const val SCREEN_CAPTURE_PERMISSION = " android.permission.DETECT_SCREEN_CAPTURE"
22- private const val SCREEN_RECORDING_PERMISSION = " android.permission.DETECT_SCREEN_RECORDING"
23- }
18+ @SuppressLint(" StaticFieldLeak" )
19+ internal object ScreenProtector {
20+ private const val TAG = " TalsecScreenProtector"
21+ private const val SCREEN_CAPTURE_PERMISSION = " android.permission.DETECT_SCREEN_CAPTURE"
22+ private const val SCREEN_RECORDING_PERMISSION = " android.permission.DETECT_SCREEN_RECORDING"
2423
25- internal var activity: Activity ? = null
2624 private var isEnabled = false
25+ private var isRegistered = false
2726
28- private val screenCaptureCallback = ScreenCaptureCallback { Talsec .onScreenshotDetected() }
27+ private val cachedThreats = mutableSetOf<Threat >()
28+ private val screenCaptureCallback = ScreenCaptureCallback { handleThreat(Threat .Screenshot ) }
2929 private val screenRecordCallback: Consumer <Int > = Consumer <Int > { state ->
30- if (state == SCREEN_RECORDING_STATE_VISIBLE ) {
31- Talsec .onScreenRecordingDetected()
32- Log .e(" ScreenProtector" , " Screen recording detected" )
33- }
30+ if (state == SCREEN_RECORDING_STATE_VISIBLE ) handleThreat(Threat .ScreenRecording )
3431 }
3532
36- internal fun enable () {
33+ fun enable () {
3734 if (isEnabled) return
35+
3836 isEnabled = true
37+ cachedThreats.forEach { handleThreat(it) }
38+ cachedThreats.clear()
3939 }
4040
41- override fun onStart (owner : LifecycleOwner ) {
42- super .onStart(owner)
43-
44- if (isEnabled) activity?.let { register(it) }
41+ fun disable () {
42+ isEnabled = false
4543 }
4644
47- override fun onStop ( owner : LifecycleOwner ) {
48- super .onStop(owner)
49-
50- if (isEnabled) activity?. let { unregister(it) }
51- }
45+ internal fun register ( activity : Activity ) {
46+ if (isRegistered) {
47+ android.util. Log .w( TAG , " ScreenProtector is already registered. " )
48+ return
49+ }
5250
53- private fun register (activity : Activity ) {
5451 if (Build .VERSION .SDK_INT >= 34 ) {
5552 registerScreenCapture(activity)
5653 }
5754
5855 if (Build .VERSION .SDK_INT >= 35 ) {
5956 registerScreenRecording(activity)
6057 }
61- }
62-
63- // Missing permission is suppressed because the decision to use the screen capture API is made
64- // by developer, and not enforced by the library.
65- @SuppressLint(" MissingPermission" )
66- private fun unregister (currentActivity : Activity ) {
67- val context = currentActivity.applicationContext
68-
69- if (Build .VERSION .SDK_INT >= 34 && hasPermission(
70- context, SCREEN_CAPTURE_PERMISSION
71- )
72- ) {
73- currentActivity.unregisterScreenCaptureCallback(screenCaptureCallback)
74- }
7558
76- if (Build .VERSION .SDK_INT >= 35 && hasPermission(
77- context, SCREEN_RECORDING_PERMISSION
78- )
79- ) {
80- currentActivity.windowManager?.removeScreenRecordingCallback(screenRecordCallback)
81- }
59+ isRegistered = true
8260 }
8361
8462 // Missing permission is suppressed because the decision to use the screen capture API is made
@@ -112,7 +90,34 @@ internal class ScreenProtector : DefaultLifecycleObserver {
11290 context.mainExecutor, screenRecordCallback
11391 )
11492 screenRecordCallback.accept(initialState)
93+ }
11594
95+ // Missing permission is suppressed because the decision to use the screen capture API is made
96+ // by developer, and not enforced by the library.
97+ @SuppressLint(" MissingPermission" )
98+ internal fun unregister (currentActivity : Activity ) {
99+ if (! isRegistered) {
100+ android.util.Log .w(TAG , " ScreenProtector is not registered." )
101+ return
102+ }
103+
104+ val context = currentActivity.applicationContext
105+
106+ if (Build .VERSION .SDK_INT >= 34 && hasPermission(
107+ context, SCREEN_CAPTURE_PERMISSION
108+ )
109+ ) {
110+ currentActivity.unregisterScreenCaptureCallback(screenCaptureCallback)
111+ }
112+
113+ if (Build .VERSION .SDK_INT >= 35 && hasPermission(
114+ context, SCREEN_RECORDING_PERMISSION
115+ )
116+ ) {
117+ currentActivity.windowManager?.removeScreenRecordingCallback(screenRecordCallback)
118+ }
119+
120+ isRegistered = false
116121 }
117122
118123 private fun hasPermission (context : Context , permission : String ): Boolean {
@@ -122,9 +127,22 @@ internal class ScreenProtector : DefaultLifecycleObserver {
122127 }
123128
124129 private fun reportMissingPermission (protectionType : String , permission : String ) {
125- Log .e(
130+ android.util. Log .e(
126131 TAG ,
127132 " Failed to register $protectionType callback. Check if $permission permission is granted in AndroidManifest.xml"
128133 )
129134 }
130- }
135+
136+ private fun handleThreat (threat : Threat ) {
137+ if (! isEnabled) {
138+ cachedThreats.add(threat)
139+ return
140+ }
141+
142+ when (threat) {
143+ Threat .Screenshot -> Talsec .onScreenshotDetected()
144+ Threat .ScreenRecording -> Talsec .onScreenRecordingDetected()
145+ else -> throw IllegalArgumentException (" Unexpected Threat type: $threat " )
146+ }
147+ }
148+ }
0 commit comments