@@ -5,6 +5,7 @@ import android.content.Context
55import android.graphics.Bitmap
66import android.graphics.Color
77import com.autoclicker.app.service.ClickerAccessibilityService
8+ import com.autoclicker.app.util.TemplateMatcher
89import com.autoclicker.app.service.ScreenCaptureService
910import com.autoclicker.app.util.Constants
1011import com.autoclicker.app.util.CrashHandler
@@ -92,6 +93,12 @@ class ScriptEngine(
9293 private val REGEX_GET_VAR = Regex (""" getVar\(["'](\w+)["']\)""" )
9394 private val REGEX_INC_VAR = Regex (""" incVar\(["'](\w+)["']\)""" )
9495 private val REGEX_DEC_VAR = Regex (""" decVar\(["'](\w+)["']\)""" )
96+
97+ // AI-Powered Commands
98+ private val REGEX_FIND_TEXT = Regex (""" findText\(["'](.*?)["']\)""" )
99+ private val REGEX_FIND_TEXT_TIMEOUT = Regex (""" findText\(["'](.*?)["'],\s*(\d+)\)""" )
100+ private val REGEX_FIND_IMAGE = Regex (""" findImage\(["'](.*?)["']\)""" )
101+ private val REGEX_FIND_IMAGE_THRESHOLD = Regex (""" findImage\(["'](.*?)["'],\s*([0-9.]+)\)""" )
95102 }
96103
97104 fun execute (code : String ) {
@@ -201,6 +208,8 @@ class ScriptEngine(
201208 line.contains(" getText(" ) -> parseGetText(line)
202209 line.contains(" getColor(" ) -> parseGetColor(line)
203210 line.contains(" pushToCb(" ) -> parsePushToClipboard(line)
211+ line.contains(" findText(" ) -> parseFindText(line)
212+ line.contains(" findImage(" ) -> parseFindImage(line)
204213 line == " EXIT = true" -> EXIT = true
205214 line.startsWith(" while" ) -> return executeWhileLoop(line, allLines, currentIndex)
206215 line.startsWith(" if" ) -> return executeIfBlock(line, allLines, currentIndex)
@@ -1129,4 +1138,186 @@ class ScriptEngine(
11291138 }
11301139 log(" Clipboard: $text " )
11311140 }
1141+
1142+ // ==================== AI-POWERED METHODS ====================
1143+
1144+ /* *
1145+ * Find text anywhere on screen using ML Kit OCR.
1146+ * Returns coordinates of found text or null if not found.
1147+ *
1148+ * @param text Text to search for
1149+ * @param timeout Maximum time to search in milliseconds (default 5000)
1150+ * @return Pair of (x, y) coordinates where text was found, or null
1151+ */
1152+ fun findText (text : String , timeout : Long = 5000): Pair <Int , Int >? {
1153+ if (EXIT || Thread .currentThread().isInterrupted) return null
1154+
1155+ log(" 🔍 FindText: searching for '$text '..." )
1156+ val startTime = System .currentTimeMillis()
1157+
1158+ while (! EXIT && ! Thread .currentThread().isInterrupted) {
1159+ val elapsed = System .currentTimeMillis() - startTime
1160+ if (elapsed >= timeout) {
1161+ log(" ⏱️ FindText: timeout after ${timeout} ms" )
1162+ return null
1163+ }
1164+
1165+ val screenshot = screenshot() ? : continue
1166+
1167+ try {
1168+ val image = InputImage .fromBitmap(screenshot, 0 )
1169+ val latch = CountDownLatch (1 )
1170+ var result: Pair <Int , Int >? = null
1171+
1172+ val recognizer = getTextRecognizer()
1173+ if (recognizer == null ) {
1174+ log(" ❌ FindText: TextRecognizer not initialized" )
1175+ screenshot.recycle()
1176+ return null
1177+ }
1178+
1179+ recognizer.process(image)
1180+ .addOnSuccessListener { visionText ->
1181+ for (block in visionText.textBlocks) {
1182+ if (block.text.contains(text, ignoreCase = true )) {
1183+ val rect = block.boundingBox
1184+ if (rect != null ) {
1185+ result = Pair (rect.centerX(), rect.centerY())
1186+ log(" ✅ FindText: found at (${rect.centerX()} , ${rect.centerY()} ) in ${elapsed} ms" )
1187+ }
1188+ break
1189+ }
1190+ }
1191+ latch.countDown()
1192+ }
1193+ .addOnFailureListener { e ->
1194+ log(" ❌ FindText error: ${e.message} " )
1195+ latch.countDown()
1196+ }
1197+
1198+ latch.await(2000 , TimeUnit .MILLISECONDS )
1199+ screenshot.recycle()
1200+
1201+ if (result != null ) {
1202+ return result
1203+ }
1204+ } catch (e: Exception ) {
1205+ log(" ❌ FindText error: ${e.message} " )
1206+ CrashHandler .logError(" ScriptEngine" , " Error in findText" , e)
1207+ screenshot.recycle()
1208+ }
1209+
1210+ sleep(200 )
1211+ }
1212+
1213+ return null
1214+ }
1215+
1216+ /* *
1217+ * Find image on screen using template matching.
1218+ * Returns coordinates of best match or null if not found.
1219+ *
1220+ * @param imagePath Path to template image file
1221+ * @param threshold Confidence threshold (0.0 to 1.0, default 0.8)
1222+ * @return Pair of (x, y) coordinates where image was found, or null
1223+ */
1224+ fun findImage (imagePath : String , threshold : Float = 0.8f): Pair <Int , Int >? {
1225+ if (EXIT || Thread .currentThread().isInterrupted) return null
1226+
1227+ log(" 🔍 FindImage: searching for '$imagePath ' (threshold: $threshold )..." )
1228+
1229+ try {
1230+ // Load template image
1231+ val templateFile = java.io.File (imagePath)
1232+ if (! templateFile.exists()) {
1233+ log(" ❌ FindImage: template file not found: $imagePath " )
1234+ return null
1235+ }
1236+
1237+ val template = android.graphics.BitmapFactory .decodeFile(imagePath)
1238+ if (template == null ) {
1239+ log(" ❌ FindImage: failed to decode template image" )
1240+ return null
1241+ }
1242+
1243+ // Get current screenshot
1244+ val screenshot = screenshot()
1245+ if (screenshot == null ) {
1246+ log(" ❌ FindImage: failed to capture screenshot" )
1247+ template.recycle()
1248+ return null
1249+ }
1250+
1251+ // Perform template matching
1252+ val matcher = TemplateMatcher .getInstance()
1253+ val matches = matcher.match(screenshot, template, threshold, maxMatches = 1 )
1254+
1255+ screenshot.recycle()
1256+ template.recycle()
1257+
1258+ if (matches.isNotEmpty()) {
1259+ val match = matches[0 ]
1260+ val centerX = match.x + template.width / 2
1261+ val centerY = match.y + template.height / 2
1262+ log(" ✅ FindImage: found at ($centerX , $centerY ) with confidence ${match.confidence} " )
1263+ return Pair (centerX, centerY)
1264+ } else {
1265+ log(" ❌ FindImage: not found (no matches above threshold)" )
1266+ return null
1267+ }
1268+
1269+ } catch (e: Exception ) {
1270+ log(" ❌ FindImage error: ${e.message} " )
1271+ CrashHandler .logError(" ScriptEngine" , " Error in findImage" , e)
1272+ return null
1273+ }
1274+ }
1275+
1276+ private fun parseFindText (line : String ) {
1277+ val matchTimeout = REGEX_FIND_TEXT_TIMEOUT .find(line)
1278+ if (matchTimeout != null ) {
1279+ val text = matchTimeout.groupValues[1 ]
1280+ val timeout = matchTimeout.groupValues[2 ].toLong()
1281+ val result = findText(text, timeout)
1282+ if (result != null && line.contains(" =" )) {
1283+ val varName = line.substringBefore(" =" ).trim().removePrefix(" val " ).removePrefix(" var " )
1284+ variables[varName] = result
1285+ }
1286+ return
1287+ }
1288+
1289+ val match = REGEX_FIND_TEXT .find(line)
1290+ if (match != null ) {
1291+ val text = match.groupValues[1 ]
1292+ val result = findText(text)
1293+ if (result != null && line.contains(" =" )) {
1294+ val varName = line.substringBefore(" =" ).trim().removePrefix(" val " ).removePrefix(" var " )
1295+ variables[varName] = result
1296+ }
1297+ }
1298+ }
1299+
1300+ private fun parseFindImage (line : String ) {
1301+ val matchThreshold = REGEX_FIND_IMAGE_THRESHOLD .find(line)
1302+ if (matchThreshold != null ) {
1303+ val imagePath = matchThreshold.groupValues[1 ]
1304+ val threshold = matchThreshold.groupValues[2 ].toFloat()
1305+ val result = findImage(imagePath, threshold)
1306+ if (result != null && line.contains(" =" )) {
1307+ val varName = line.substringBefore(" =" ).trim().removePrefix(" val " ).removePrefix(" var " )
1308+ variables[varName] = result
1309+ }
1310+ return
1311+ }
1312+
1313+ val match = REGEX_FIND_IMAGE .find(line)
1314+ if (match != null ) {
1315+ val imagePath = match.groupValues[1 ]
1316+ val result = findImage(imagePath)
1317+ if (result != null && line.contains(" =" )) {
1318+ val varName = line.substringBefore(" =" ).trim().removePrefix(" val " ).removePrefix(" var " )
1319+ variables[varName] = result
1320+ }
1321+ }
1322+ }
11321323}
0 commit comments