Untitled
unknown
kotlin
8 months ago
7.9 kB
9
Indexable
Never
package com.cct.muganim.services import android.accessibilityservice.AccessibilityService import android.app.AlarmManager import android.app.PendingIntent import android.content.Intent import android.net.Uri import android.os.Build import android.os.SystemClock import android.util.Log import android.util.Patterns import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo import com.cct.muganim.utils.Constant import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.BufferedReader import java.io.InputStreamReader import java.net.URLDecoder class CustomAccessibleService : AccessibilityService() { private var browserApp = "" private var browserUrl = "" private val serviceScope = CoroutineScope(Dispatchers.Default) override fun onTaskRemoved(rootIntent: Intent?) { val restartServiceIntent = Intent(applicationContext, this.javaClass) val restartServicePendingIntent = PendingIntent.getService( applicationContext, 1, restartServiceIntent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE } else { PendingIntent.FLAG_ONE_SHOT } ) val alarmService = applicationContext.getSystemService(ALARM_SERVICE) as AlarmManager alarmService[AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000] = restartServicePendingIntent } override fun onAccessibilityEvent(event: AccessibilityEvent?) { event ?: return when (event.eventType) { AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, AccessibilityEvent.TYPE_VIEW_CLICKED, AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, AccessibilityEvent.TYPE_VIEW_SCROLLED -> processEvent(event) } } private fun processEvent(event: AccessibilityEvent) { val parentNodeInfo = event.source ?: return // Early return if source is null val packageName = event.packageName.toString() // Determine whether to delay URL capture based on the event type if (event.eventType == AccessibilityEvent.TYPE_VIEW_CLICKED || event.eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) { // Delay capturing the URL to allow time for the address bar to update serviceScope.launch { delay(500) // Delay in milliseconds; adjust based on observed behavior captureUrlCommonLogic(parentNodeInfo, packageName) } } else { // Proceed to capture the URL without delay captureUrlCommonLogic(parentNodeInfo, packageName) } } private fun captureUrlCommonLogic(nodeInfo: AccessibilityNodeInfo, packageName: String) { // Consolidate common logic for URL capture here to avoid duplication serviceScope.launch { try { getSupportedBrowsers().firstOrNull { it.packageName == packageName }?.let { browserConfig -> captureUrl(nodeInfo, browserConfig)?.let { capturedUrl -> if (Patterns.WEB_URL.matcher(capturedUrl).matches() && browserUrl != capturedUrl) { withContext(Dispatchers.Main) { browserUrl = capturedUrl browserApp = packageName putMyUrl(capturedUrl, packageName) } } } } } finally { withContext(Dispatchers.Main) { nodeInfo.recycle() // Recycle on the main thread } } } } private suspend fun putMyUrl(url: String, browser: String) { // Decode URL and convert it to URI for easier parsing val decodedUrl = withContext(Dispatchers.IO) { URLDecoder.decode(url, "UTF-8") } val uri = Uri.parse(decodedUrl) // Extract the 'q' query parameter from the URL, or use null if it's not present if (uri.isHierarchical) { val searchQuery = uri.getQueryParameter("q")?.toLowerCase() // Check if the extracted search query matches any blocked keywords if (searchQuery != null && Constant.blockedKeywordsList.any { keyword -> searchQuery.contains( keyword.toLowerCase() ) } && !overlayRecentlyClosed) { Log.i(Constant.TAG, "Blocked keyword found in search query. Showing overlay.") withContext(Dispatchers.Main) { showBlockingOverlay() } } } else { Log.i(Constant.TAG, "URL is not hierarchical and cannot be processed: $decodedUrl") } } companion object { var overlayRecentlyClosed = false } private fun showBlockingOverlay() { if (!overlayRecentlyClosed) { val overlayIntent = Intent(this, FloatingLayoutService::class.java) startService(overlayIntent) } } private fun captureUrl(info: AccessibilityNodeInfo, config: SupportedBrowserConfig): String? { Log.d(Constant.TAG, "Trying to capture URL for package: ${config.packageName}") val nodes = info.findAccessibilityNodeInfosByViewId(config.addressBarId) if (nodes.isNullOrEmpty()) { Log.d(Constant.TAG, "No nodes found for ID: ${config.addressBarId} in package: ${config.packageName}") return null } return nodes.firstOrNull()?.text?.toString().also { url -> Log.d(Constant.TAG, "Captured URL: $url for package: ${config.packageName}") } } private fun getSupportedBrowsers(): List<SupportedBrowserConfig> { return listOf( SupportedBrowserConfig("com.android.chrome", "com.android.chrome:id/url_bar"), SupportedBrowserConfig("org.mozilla.firefox", "org.mozilla.firefox:id/mozac_browser_toolbar_url_view"), SupportedBrowserConfig("com.opera.browser", "com.opera.browser:id/url_field"), // add the other ones... ) } data class SupportedBrowserConfig(val packageName: String, val addressBarId: String) private fun loadBlockedKeywords() { serviceScope.launch(Dispatchers.IO) { val keywords = readWordsFromAssetFile("extended_test_set_keywords.txt") withContext(Dispatchers.Main) { Constant.blockedKeywordsList.clear() Constant.blockedKeywordsList.addAll(keywords) Log.i(Constant.TAG, "Blocked Keywords Reloaded: ${keywords.size}") } } } private suspend fun readWordsFromAssetFile(fileName: String): HashSet<String> = withContext(Dispatchers.IO) { val keywords = HashSet<String>() try { applicationContext.assets.open(fileName).use { inputStream -> BufferedReader(InputStreamReader(inputStream)).use { reader -> reader.forEachLine { line -> keywords.addAll(line.split("\\s+".toRegex())) } } } } catch (e: Exception) { Log.e(Constant.TAG, "Error reading blocked keywords from file: $fileName", e) } keywords } override fun onInterrupt() { Log.i(Constant.TAG, "Accessibility Service Interrupted") } override fun onServiceConnected() { super.onServiceConnected() Log.i(Constant.TAG, "Accessibility Service Connected") loadBlockedKeywords() } }
Leave a Comment