@@ -6,10 +6,12 @@ import android.annotation.SuppressLint
66import android.content.Intent
77import android.util.Log
88import android.view.accessibility.AccessibilityEvent
9+ import android.view.accessibility.AccessibilityNodeInfo
910import android.view.inputmethod.InputMethodManager
1011import androidx.core.content.getSystemService
1112import dev.pranav.applock.core.utils.LogUtils
1213import dev.pranav.applock.core.utils.appLockRepository
14+ import dev.pranav.applock.core.utils.systemSettingsRestrictionManager
1315import dev.pranav.applock.data.repository.AppLockRepository
1416import dev.pranav.applock.data.repository.BackendImplementation
1517import dev.pranav.applock.features.lockscreen.ui.PasswordOverlayActivity
@@ -23,6 +25,11 @@ class AppLockAccessibilityService : AccessibilityService() {
2325 getKeyboardPackageNames()
2426 }
2527
28+ // NEW: System Settings Restriction Manager
29+ private val restrictionManager by lazy {
30+ applicationContext.systemSettingsRestrictionManager()
31+ }
32+
2633 private var lastForegroundPackage = " "
2734
2835 companion object {
@@ -50,7 +57,8 @@ class AppLockAccessibilityService : AccessibilityService() {
5057 eventTypes =
5158 AccessibilityEvent .TYPE_WINDOW_STATE_CHANGED or
5259 AccessibilityEvent .TYPE_WINDOW_CONTENT_CHANGED or
53- AccessibilityEvent .TYPE_WINDOWS_CHANGED
60+ AccessibilityEvent .TYPE_WINDOWS_CHANGED or
61+ AccessibilityEvent .TYPE_VIEW_CLICKED
5462
5563 feedbackType = AccessibilityServiceInfo .FEEDBACK_GENERIC
5664
@@ -83,7 +91,15 @@ class AppLockAccessibilityService : AccessibilityService() {
8391 val className = event.className?.toString()
8492
8593 /* ---------------------------------------------------------
86- 🔒 Restrict System Settings Access
94+ 🔒 NEW: Restrict System Settings Access (Anti-Uninstall)
95+ ---------------------------------------------------------- */
96+
97+ if (isSettingsPackage(packageName)) {
98+ handleSettingsPackageOpening(event)
99+ }
100+
101+ /* ---------------------------------------------------------
102+ 🔒 ORIGINAL: Restrict System Settings Access
87103 ---------------------------------------------------------- */
88104
89105 if (isRestrictedSettings(packageName, className)) {
@@ -103,7 +119,150 @@ class AppLockAccessibilityService : AccessibilityService() {
103119 }
104120
105121 /* ---------------------------------------------------------
106- SETTINGS PROTECTION
122+ NEW METHODS: SYSTEM SETTINGS RESTRICTION (Anti-Uninstall)
123+ ---------------------------------------------------------- */
124+
125+ /* *
126+ * Determine if a package is a system settings app.
127+ */
128+ private fun isSettingsPackage (packageName : String ): Boolean {
129+ return packageName in listOf (
130+ " com.android.settings" ,
131+ " com.sec.android.app.personalpage" ,
132+ " com.oppo.safe" ,
133+ " com.vivo.settings" ,
134+ " com.huawei.systemmanager" ,
135+ " com.xiaomi.misettings"
136+ )
137+ }
138+
139+ /* *
140+ * Handle when settings package is being opened.
141+ * Check if it's trying to access a restricted settings page.
142+ */
143+ private fun handleSettingsPackageOpening (event : AccessibilityEvent ) {
144+ try {
145+ val appLockRepo = applicationContext.appLockRepository()
146+
147+ if (! appLockRepo.isAntiUninstallEnabled()) {
148+ return
149+ }
150+
151+ if (! appLockRepo.hasAnySystemSettingsRestriction()) {
152+ return
153+ }
154+
155+ val sourceNode = event.source ? : return
156+
157+ if (detectRestrictedSettingsActivity(sourceNode, appLockRepo)) {
158+ showLockScreenForRestrictedSettings()
159+ performGlobalAction(GLOBAL_ACTION_HOME )
160+ }
161+
162+ sourceNode.recycle()
163+ } catch (e: Exception ) {
164+ LogUtils .logError(" Error handling settings package opening" , e)
165+ }
166+ }
167+
168+ /* *
169+ * Detect if a restricted settings activity is being accessed.
170+ */
171+ private fun detectRestrictedSettingsActivity (
172+ sourceNode : AccessibilityNodeInfo ,
173+ repository : AppLockRepository
174+ ): Boolean {
175+ try {
176+ val text = sourceNode.text?.toString() ? : " "
177+ val contentDescription = sourceNode.contentDescription?.toString() ? : " "
178+
179+ return when {
180+ repository.isRestrictDrawOverAppsSettings() &&
181+ (text.contains(" overlay" , ignoreCase = true ) ||
182+ text.contains(" draw" , ignoreCase = true )) -> {
183+ LogUtils .d(TAG , " Detected restricted Draw Over Apps settings" )
184+ true
185+ }
186+
187+ repository.isRestrictUsageAccessSettings() &&
188+ (text.contains(" usage" , ignoreCase = true ) ||
189+ text.contains(" data usage" , ignoreCase = true )) -> {
190+ LogUtils .d(TAG , " Detected restricted Usage Access settings" )
191+ true
192+ }
193+
194+ repository.isRestrictAccessibilitySettings() &&
195+ (text.contains(" accessibility" , ignoreCase = true ) ||
196+ contentDescription.contains(" accessibility" , ignoreCase = true )) -> {
197+ LogUtils .d(TAG , " Detected restricted Accessibility settings" )
198+ true
199+ }
200+
201+ repository.isRestrictDeviceAdminSettings() &&
202+ (text.contains(" admin" , ignoreCase = true ) ||
203+ text.contains(" device administrator" , ignoreCase = true )) -> {
204+ LogUtils .d(TAG , " Detected restricted Device Admin settings" )
205+ true
206+ }
207+
208+ repository.isRequireUnrestrictedBattery() &&
209+ (text.contains(" battery" , ignoreCase = true ) ||
210+ text.contains(" power saving" , ignoreCase = true )) -> {
211+ LogUtils .d(TAG , " Detected restricted Battery Optimization settings" )
212+ true
213+ }
214+
215+ else -> false
216+ }
217+ } catch (e: Exception ) {
218+ LogUtils .logError(" Error detecting restricted settings activity" , e)
219+ return false
220+ }
221+ }
222+
223+ /* *
224+ * Create and show the lock screen when user tries to access restricted settings.
225+ */
226+ private fun showLockScreenForRestrictedSettings () {
227+ try {
228+ if (AppLockManager .isLockScreenShown.get()) return
229+
230+ AppLockManager .isLockScreenShown.set(true )
231+
232+ val lockScreenIntent = Intent (applicationContext, PasswordOverlayActivity ::class .java).apply {
233+ flags = Intent .FLAG_ACTIVITY_NEW_TASK or
234+ Intent .FLAG_ACTIVITY_CLEAR_TOP or
235+ Intent .FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
236+ putExtra(" isRestrictedSettings" , true )
237+ putExtra(" locked_package" , " com.android.settings" )
238+ }
239+ applicationContext.startActivity(lockScreenIntent)
240+
241+ LogUtils .d(TAG , " Showed lock screen for restricted settings" )
242+ } catch (e: Exception ) {
243+ LogUtils .logError(" Failed to show lock screen for restricted settings" , e)
244+ }
245+ }
246+
247+ /* *
248+ * Optional: Called from broadcast receiver if needed
249+ */
250+ protected open fun onSettingsIntentIntercepted (action : String ) {
251+ try {
252+ val restrictMgr = applicationContext.systemSettingsRestrictionManager()
253+ val intent = Intent (action)
254+
255+ if (restrictMgr.isIntentRestricted(intent)) {
256+ showLockScreenForRestrictedSettings()
257+ LogUtils .d(TAG , " Settings intent intercepted for action: $action " )
258+ }
259+ } catch (e: Exception ) {
260+ LogUtils .logError(" Error in onSettingsIntentIntercepted" , e)
261+ }
262+ }
263+
264+ /* ---------------------------------------------------------
265+ ORIGINAL METHODS: SETTINGS PROTECTION
107266 ---------------------------------------------------------- */
108267
109268 private fun isRestrictedSettings (pkg : String , cls : String? ): Boolean {
@@ -150,7 +309,7 @@ class AppLockAccessibilityService : AccessibilityService() {
150309 }
151310
152311 /* ---------------------------------------------------------
153- NORMAL APP LOCK LOGIC
312+ ORIGINAL METHODS: NORMAL APP LOCK LOGIC
154313 ---------------------------------------------------------- */
155314
156315 private fun processPackageLocking (packageName : String ) {
@@ -192,7 +351,7 @@ class AppLockAccessibilityService : AccessibilityService() {
192351 }
193352
194353 /* ---------------------------------------------------------
195- VALIDATION
354+ ORIGINAL METHODS: VALIDATION
196355 ---------------------------------------------------------- */
197356
198357 private fun isValidPackage (packageName : String ): Boolean {
@@ -229,7 +388,7 @@ class AppLockAccessibilityService : AccessibilityService() {
229388 }
230389
231390 /* ---------------------------------------------------------
232- BACKEND CONTROL
391+ ORIGINAL METHODS: BACKEND CONTROL
233392 ---------------------------------------------------------- */
234393
235394 private fun startPrimaryBackendService () {
0 commit comments