From ef0ee91fd91afb392ed972189c5ea6b9c29d0c84 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Tue, 30 Jun 2026 14:19:01 +0300 Subject: [PATCH] Update: 2026-06-30 14:19:01 --- .../executionHistory/executionHistory.bin | Bin 955958 -> 966203 bytes .../executionHistory/executionHistory.lock | Bin 17 -> 17 bytes .../.gradle/8.13/fileHashes/fileHashes.bin | Bin 72965 -> 72965 bytes .../.gradle/8.13/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../8.13/fileHashes/resourceHashesCache.bin | Bin 23053 -> 23427 bytes .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../service/ScraperAccessibilityService.kt | 154 +++++++++++++----- 7 files changed, 109 insertions(+), 45 deletions(-) diff --git a/android_bot/.gradle/8.13/executionHistory/executionHistory.bin b/android_bot/.gradle/8.13/executionHistory/executionHistory.bin index 83234c17e6a806bd2aa468934a7254f53534f2a2..be3083519ad8ebea456e7cb09e692266c0272549 100644 GIT binary patch delta 3152 zcmb_edpuNm8=o`AIWsuN!C*#`ij)^&YLZ;LD6uYWZ~x!77ui&z zO_s!I>Ec>gMOj;0+Nq_MQp_S$c+VO8`Mmp1pHF|ie>{Jj=lk4#-}C&Q=T}i=TlLvi z%!bsDXkS5TG%V7ZMw`o^(P&MjHSW5{xSU6H2|x86C*vIlB85(h5-AK)l!`HXe!0$xbW_V`7+L#=t?yzQY2LWJ>KtapdUoHRUBP$D`NDvc4!wcQ`O(WqW z7egg~WsWr2dQ+CbX$-buFMROR)*8q6yGISK@td#p5pv|v80^e`pF^)*KIZq^jfFED z#n0Du$dQ+0up=knN#41h<1dyz?=P}icRb2@DsKZH2p%yFz>5SF1$YBf8Ep9oYck|+ zdq2YS%sRr_3lo1(84ZeJsSOR+@u%D>hv-T)d=o0+n5VxuyCP*31Npu)4YYrRi}`N0 zU1jI%?r-#TtG>ED*E5YYl&!XOp0ryOmv?Bj(M26`;1kTny}B7$`;$#lG71mq_(umn1>Dc@ubiwkH!F0TYZ|r3&Vqa8tpiz= z6nV*ppmZG81qS1Axt?E{@6uWRgEO%=c8J~5jlwCDG#>~6!wFaiC{4iM;xVbM)s}C9 z>o%zV&JR!dG#`Xaz{YIe%jR!!#4iq6(!R2%V6HTkj1In^fZKHI{me5eiWtV_`n|vQ zXPeHV5E(BNq));b>>EyRk8SQf-&XNj`vc!A|h z6QbW@~$S-=AZ7%(JI`2ij zd4w>e(LMj~y36^{Nr1zMHb4aDZ3^Ijwt^cRW&aJa`hjJHS$ZUwdfECsoNGCScjk=+9IP%JP45?huIL zf@3TMu{6AuD4nF=ojKC^Nbh!ix4QdOl`Z!mDEq&$x$>h{5CM|T;Q+&^QDJ0j#8XIK z-Z+XZ2yU0nNnmR7CHePs&uSJ(O~_8kJU1$HDu#c)+NZm9Wv`78`i>m2vF3qsVWJ?5 zz%a5>p&-dVB!BSTc=?=h`%9yp`{d1_N&2cycI&!d0Kd$c=XgZD|B?vXA9b<2oeV z8rRyD+y5+gi;|#WGjt}o+*q0jDZugmB-&a?{Xqud=QiM2$=s_?lXm%R$jlwlxsPvXTkY-K+{`$X)UkYj=(XHcEQvLJS*vru)$zFTCcA2`9f%+B-|S4Fp6_^sl) zO^-f5{NwAJB(g%n+B4g@RolZ%w)>rWUE_^atH2_X?H~6J#N~8&x~SQ;{fMzYgo7># zD@Cu&&T5FMIlSP^`GEi{VHrHrm48rp(bDjqqY(ston;ZPPF-85QHmkapI*f#)NRi6;4!lqv{Z@xW5VM zE0j*h756jY%tTi!f6*lX+R6GiF@U1EN<^gtCW45rQ0z8cFZ0v;o<6SD??`FPwez8$ zev%eZ7qnXiDwV%R0G@%tzsq0wl^3G{WmqEgDq=!CZ95ngF4Z1MXmt^W4gf53Y|?j=wRiH zFUb+CN7D(XqgX%POmR3g%YW31cIxsO2ff-&)Ry(X8lTABC8=ltchb=YUB!7pjnTO@ F=sy{ti39)u delta 2738 zcmb_edo)yQ8=q}v_MXA)Hb%yXI;oT*QA!tgMW`f6$E6W647#}Fau7o0GIDw8qI{|E zqm&74*P>EHF6AVrPEjd|GL_4?b@q()t<|T0zdxR}_IiGo=Xu`suJ_qhlk0C?ThC`g zXqw_xs7BCksg%}@r7N?ZNcro=4zdykSr8FYM39M~5JANUS@8K_Dw#xzA_F6Tblzg~ znyqJ!+CydbPg}Gv%`0!Gk&4(5DVUI;`k1_)bFR3jbsL%_K zUyBrfmkr0=hKdtI=4% z?>j7_^v0A4aGzm#6Pc1je1fZEupV2pb(xQ;S#AYeVdMI^;SV8a2t=rN2Z2fe+jVPfI5)SyMOHsUYY|%54df zB!W$t9kuP=c{iSS`mJ;dlV7XZC3XirBG`x(Wm;B3@ikYHkKfKoT-M$?Q@36paGucA zKz9U#0kC=+6?l)s@)U_IavWZ^!ue|Qo1$%U?<#GU^0=Og0pe0Zy{1o6WD?eArt6lz zqQtvY>s(}el=Ug_bR52}zx2%#Mtn^bTl=-jUZX6Hn*{M%`Jm4C2!HQ&Tl!xY8`vEp zQvcepU39&A-IS0yOe(c0?grb7Y5HVww?h-KGM1QC+@X~9DZKZ>o1531V0EB82|r_X z^Ea;zwHTp>I9NyV;sX8}lQa{(00^dFC7?D1m$7%HI6MuLpICCIbbi$MgHy|AC{x-H z01Kw!xnP$JA`cX&VH1qnp|5&vf6$q{5@gH!-*dHcbh1`N-Re zd=A`Defoa%kkSP{ijzKymb61284RO(oinoK!_?Zhmc@qfhFbg*e$8)Z4Dl6 zI>Vvgzl)2uK!m*Bwf&RFaLFidFnnCE8eE-`$Id6@C>c@3g;K~Ph_j&4F zQeeGJzNwf6S}90AORYzvk&QbzMZh$27{WIuEMvk#*q*{d@C+??6B zR(^GlM1CAWidnn4HI=VPZd*TkXm&(wj8K}9pOpf(G{lE#{-j*{#5vUKbfoB(fM)z& zqHCfd8-r0(+(#LQKq9cnaGr5Pl-d&VQ^b=u|Wvs?KF%rZ@ z7MX8s8D3E6Y~oVyQI~9M-NtmDnXifhrExvPj32(q~xnb%lq+`7L9gqxj~M?tUnE&XuETXM#;^~Wim3IyU?7wO?&maJ1Zr}BTr~K ziD5T7%Nn(OMn&vlh4qsZh*(`%`l0A0Aa@bX3IEHKrm`E?@kAE80%LzvWl6Q`(oGZAQRKdY!XGkUBPy_KCJiL!A!8`9EKDyYQxxzyg?xUv48!RcEF@ho7ZUkvu z_{+cI{mAkRo8E`NWRsqIUR0Oy2rd!J? zbkr6$TQBM_M9JaWx~_Mdp%h)f52Q!qnql-78La8>-iQ~yHGlk;@}FYk^EL|UoNFea zHj1u|hs4tvb0}XEiCd7ENiSgD>%L+zDl4*0A%(k)NGYk+ixG<~dbs)vPag}FX2oTx z`jY`C%g)6Rr^sMzG3-u=S^8z#M?ig_^cuX)q90=`mGXDjDY_mYXMNE{pfjuXZJ;2sH=AH4G}4 z_KS|HhVeaweJ{nAZ+$#2CD?99j^19At0P)tzX=P#@I^z zjaAXR4<2qRNiHC+3%74)w)Ln4M4uAK2vv3W{G(9O!b^K9y=|2s(+x`Fm diff --git a/android_bot/.gradle/8.13/executionHistory/executionHistory.lock b/android_bot/.gradle/8.13/executionHistory/executionHistory.lock index b5566cd5014ff586641e28473df31591cb677a2a..695415d2ebc90a2ae68b46d8421aefa51d045a38 100644 GIT binary patch literal 17 VcmZSHW4!avD({VX3}C=i2>?7{1vdZy literal 17 VcmZSHW4!avD({VX3}C<%2LL=q1snhX diff --git a/android_bot/.gradle/8.13/fileHashes/fileHashes.bin b/android_bot/.gradle/8.13/fileHashes/fileHashes.bin index aef68768be6513441d2ba72f59035125ee443705..e6c4a2de9135194e275560209e1c181ff6d8375b 100644 GIT binary patch delta 1822 zcmZXU4K!3~7{_OfOyqkSt{IK1FNZBplIw<)Zp+=5Qn@y2D)E1(tcg&c~ zFe4NCI+)CePDPHeHrdIpibS?dB*Koiti3aL#;MtR&U@c`&h!8Op67ku_r0g<^-tI9 zSIwS>`_d{*$Mf$fcE&%oBE=+$ zlC@F~a(i_9P~+yY<1>^R_x74mncB(Z4+1x!QVxK4K!c)sM8aYAOP{{?*QvqmWbb*= zuYd%1ACOn1Up1PEO*S;1nttG}4Wha0H-L$K1OTxYBeX%VnoY}KL}z%#i6`I9)~=lO zx-!#?8U%W%0b_~G-?}0%M`YNmhPl9%on$$?f6laJ+|X>S>72%aM}n&72r_gW8=_X8 z`tyy0nNLfP#49(lU+qW;I*KU4RDm@lChmBs$jq?1f0u0nDZBsC5<#PY1QeI8!QO&u zNGgls^iD^?$uo?kPUbVL29g3ScqHgP(|e|kS=Dd&c+>WafxPi~!bAZHaD_x1Q-BAm zZ)cu0sN;|!xznQnFS}d##6#3)UM{9jGzJ4M)fP(v=Mj}HmkbG&=lEUwV<`RcqbocD zUxm)++{GoqowDjVy%u|A@9(6$%|m9$_9ENN6d0+RfWa_2fCvz*IsM-4^`TNP57wh& z&AT?v*M+Ri?*zzPR!$2UpD;Q`$24??d=1#5qUba-BFFQBpXX0GzP90W<U*e#f zYpAq*#oKEs=`|nAP8K=M2ayJ;S9H9P425oGO&s~fW=FZOG3xs}hd--D2|+2U=D@UW z`DR&1bQAl>te(#qExB5PsuBza=M3Hyra-O(?LU~~!;a!N%C`@fe}ir&Q-A~X5-RB7 zkwIsPwWNoaiiL_-DaD=5O}x068+41NymuPh0@b!kvWl9p&}8%!uRD8dI#p=wa>G6S zp+;e#QuuYz%*%coWvyE?;2@I}1`TP0Xf>29WY%dtJmIlHk?-Ifs$sVIFceNC)cd#Uw;wsQ6La1ph_ z&0#KQq}TOV)Y>Zkc%SdIR3pHlg<#^rKmfrmFSj@8<_E$)=b(s@WLbc=22_I;f$gOg zuAq6toA>{RyCoZzb`#OvFMtyYEP?+Z$Vn9|hcpwVm2a zXQVy}oU#l!OcLs|Gl}vCdlW_DP^!El?D9*PkWYiko<=N*d(M`ip$G$-yJGV3fM`_tI)s14x-zWPW_Y5b?#=n{e>xd+JD z!HhA!UX$;~u%~rycAHs!!QEf;OjZA59bRMgqS=z@li1>2XFT~E7aC}IVQ2-$%m44F z*y-M;Jg6*-=X}Ukwr!w)kz)a(Hj@B)9AVk|ZKT2p>3W;Az=+Yan{u>eYOtd!?0+yM Bh&TWM delta 1872 zcmZXUdpJ~S9LLX?u}YdSF~>|eyNb||N_I+HGpO8hiLOI8(zGrmk;|HL>Cq$1_8K!5 z$!2WBD($2)>B6o^uDgo1+OSG4CHsVs+IP$u4|<>HJ?|gC&-eR#-|so^NtBTiWu%gc zG|X#Lt*E_k;>i4xOlC)U{c{8%eln54@&`qHOBDtS_=@S$5x%hs z%1Mp>q<+@ex zZ^Z>`6aqvWt`;ld!xYZ14TFvjYCC(=;&JG>{77&RfrrQxEE3XDOAki6UZa7D zt=|<)+VV&4)IQvVG^b|=nwSW8S$*8-?H{e0Y|ki*3KMfXxX#MPD{ilkEUP}hK~%sx zk~N_FXAbwtOeboaFls|%uYwfOkt;n`Sb*Os^L&5@W{0SZJBh?m`jzm zWkwQE^9q-dc6MHaGuj_#=(r;YCy&Wryfi+ml7LD!lf)mct9;s0lTAK&E_U?#)j1npDHU=%Ltlrv zm2-@i2V9@C2h2a_1rG(UErWGz(hVU4caa$g&!b~UlDU0q1BrL&z2{a*o^9`$$*KAS zD}gCUI`4{YIp9+HC_0*k$zpv?eAWy-j zpD$qs9VQQGH@r_WzOl>a>~H;UCS-g9c(_tPNw%)^+lx8`w4J}vCemS8HNxAO7nl(8 zH%>zeei|A1Jj^TA`>!bNapSw!40&Vslg*WYVwcn(q}JjLBBl}RywOo@jm%rT`n$S3 zDPKX^H7lsB_n_~RJ+@+pKx$w9RhggVi4)r=kb??{{X_v#YCzsT3YD;Q6H?mtCP;tr z*;iO{25!^}A|)kQ%e}%n=f%B+WMZ_%3g5~)MbREp7`zlo9SZ}3hz$EsVHTZ0Lw|2C zV2)^vx~3x0KEo`Ao_ECjqe|YBaIU7IYePBiSFPBA#^Ns( z>sZ4b{Rbl-TznUN>+{XG4tNheNUnu*f%Hp>u;jJAII>62%S9YiyS1nSZ&C|MI5hg< G!{UGQ_G~Qx diff --git a/android_bot/.gradle/8.13/fileHashes/fileHashes.lock b/android_bot/.gradle/8.13/fileHashes/fileHashes.lock index 262bfb605e575b2ef68fd2a701a6e63e07b658da..913ffc033444775fff5b783f220605f4058887d4 100644 GIT binary patch literal 17 VcmZQpDUVz8=j>v01~3r64gfVy1v&r# literal 17 VcmZQpDUVz8=j>v01~3pe1pqXx1egE- diff --git a/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin b/android_bot/.gradle/8.13/fileHashes/resourceHashesCache.bin index 943d821973f48d64b50f84601faf3bdd4b046ce8..5b3e19689536225dc781aaffebb9dfdc341ddee1 100644 GIT binary patch delta 481 zcmeC(!q~i>af6A3=EMf?m6{t8dS)!@G_g%#(<+E~RPkn42@OVpDIq(J z=Wl0O4;7udIaShuQD7zCJpIYd2i`(NqZ233l?o8p@?lR{%%MwlP^G^&Kb4AL5%}|b zrd4~jR5?_s>}FGW2_^wm#ffdtx8_lQ5H*1&zwCU0H5HhjtAH}$n5hYnz>5U!Z_{`R~K=ES4%jI$dy IYyznO0J6NCq5uE@ delta 92 zcmZqP&e*$!af6A3U|WOtO3e)kJu?`!763j&1&{y$ diff --git a/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt b/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt index c8642adb..2175a2f6 100644 --- a/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt +++ b/android_bot/app/src/main/java/com/siro/android_bot/service/ScraperAccessibilityService.kt @@ -46,6 +46,11 @@ class ScraperAccessibilityService : AccessibilityService() { private var taxiFPriceScrollAttempts = 0 private var taxiFReadingPriceStartTime = 0L + // Jeeny State Memory + private var jeenyDestinationDone = false + private var jeenyPickupDone = false + private var jeenySearchTypingTime = 0L + private var jeenyPickupWaitStartTime = 0L companion object { private val TAXIF_EXCLUDED_TEXTS = setOf( "JOD", "ECO", "TaxiF", "SUV", "EV", "Mini", "Female", "Van", @@ -872,6 +877,28 @@ class ScraperAccessibilityService : AccessibilityService() { return null } + private fun getJeenyEcoLitePrice(rootNode: android.view.accessibility.AccessibilityNodeInfo?): Double? { + if (rootNode == null) return null + val ecoNode = findNodeByText(rootNode, "Eco lite") + ?: findNodeByText(rootNode, "EcoLite") + ?: findNodeByText(rootNode, "ايكو لايت") + ?: findNodeByText(rootNode, "إيكو لايت") + + if (ecoNode != null) { + var parent = ecoNode.parent + for (i in 0..2) { // search up to 3 levels up + if (parent == null) break + val prices = mutableListOf>() + findAllJODPrices(parent, prices) + if (prices.isNotEmpty()) { + return prices.first().first + } + parent = parent.parent + } + } + return null + } + private fun handleJeenyAutomation(rootNode: android.view.accessibility.AccessibilityNodeInfo) { val task = currentTask ?: return Log.d(TAG, "Jeeny Automation event. State: $currentState") @@ -879,7 +906,8 @@ class ScraperAccessibilityService : AccessibilityService() { when (currentState) { BotState.SEARCHING_START -> { // Look for the "Where to" button/view on the main screen - val whereToNode = findNodeByText(rootNode, "إلى اين تريد الذهاب") + val whereToNode = findNodeByText(rootNode, "موقع الوصول") + ?: findNodeByText(rootNode, "إلى اين تريد الذهاب") ?: findNodeByText(rootNode, "Where to") ?: findNodeByText(rootNode, "إلى أين") @@ -892,10 +920,18 @@ class ScraperAccessibilityService : AccessibilityService() { if (clickableParent != null) { clickableParent.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK) Log.i(TAG, "Jeeny: Clicked where to button/view.") + jeenyDestinationDone = false + jeenyPickupDone = false + jeenySearchTypingTime = 0L + jeenyPickupWaitStartTime = 0L currentState = BotState.SEARCHING_END } else { whereToNode.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK) Log.i(TAG, "Jeeny: Clicked where to button/view (direct).") + jeenyDestinationDone = false + jeenyPickupDone = false + jeenySearchTypingTime = 0L + jeenyPickupWaitStartTime = 0L currentState = BotState.SEARCHING_END } } else { @@ -908,57 +944,77 @@ class ScraperAccessibilityService : AccessibilityService() { } } BotState.SEARCHING_END -> { - val endLoc = task.optString("end_location", "Airport") - val firstWord = endLoc.split(" ").firstOrNull() ?: "" - val resultNode = if (firstWord.isNotEmpty()) findNodeByText(rootNode, firstWord) else null - val isEditText = resultNode?.className == "android.widget.EditText" || resultNode?.isEditable == true - - // If a search result item containing the target location words is found (not the input field itself) - if (resultNode != null && !isEditText) { - var clickableParent = resultNode - while (clickableParent != null && !clickableParent.isClickable) { - clickableParent = clickableParent.parent - } - if (clickableParent != null) { - clickableParent.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK) - Log.i(TAG, "Jeeny: Clicked search result containing '$firstWord'.") - currentState = BotState.READING_PRICE - return - } - } - - // Otherwise, enter the locations to populate the list - val editTexts = findAllEditTexts(rootNode) - if (editTexts.isNotEmpty()) { - val startLoc = task.optString("start_location", "Amman") + if (!jeenyDestinationDone) { + val endLoc = task.optString("end_location", "Airport") - if (editTexts.size >= 2) { - val pickupField = editTexts[0] - val destField = editTexts[1] - - if (pickupField.text?.toString() != startLoc) { - val pickupArgs = android.os.Bundle().apply { - putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc) - } - pickupField.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, pickupArgs) - Log.i(TAG, "Jeeny: Set pickup -> $startLoc") - } - - if (destField.text?.toString() != endLoc) { + val editTexts = findAllEditTexts(rootNode) + if (editTexts.isNotEmpty()) { + val destField = editTexts.last() // Destination is usually the last one + if (destField.text?.toString() != endLoc && !destField.text.toString().contains(endLoc)) { val destArgs = android.os.Bundle().apply { putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc) } destField.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, destArgs) Log.i(TAG, "Jeeny: Set destination -> $endLoc") + jeenySearchTypingTime = System.currentTimeMillis() + return } - } else { - val destField = editTexts[0] - if (destField.text?.toString() != endLoc) { - val destArgs = android.os.Bundle().apply { - putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, endLoc) + } + + if (jeenySearchTypingTime > 0 && System.currentTimeMillis() - jeenySearchTypingTime > 1500) { + val firstWord = endLoc.split(" ").firstOrNull() ?: endLoc + if (clickFirstSuggestion(rootNode, firstWord) || clickFirstGenericSuggestion(rootNode)) { + Log.i(TAG, "Jeeny: Clicked destination suggestion") + jeenyDestinationDone = true + jeenyPickupWaitStartTime = System.currentTimeMillis() + jeenySearchTypingTime = 0L + return + } + } + } else if (!jeenyPickupDone) { + // Wait 2 seconds for pickup widget to appear + if (jeenyPickupWaitStartTime > 0 && System.currentTimeMillis() - jeenyPickupWaitStartTime < 2000) { + return + } + + val startLoc = task.optString("start_location", "Amman") + + // The pickup search box might just be an EditText or we might need to click "موقع الانطلاق" first + val pickupTextNode = findNodeByText(rootNode, "موقع الانطلاق") + if (pickupTextNode != null) { + var clickableParent = pickupTextNode + while (clickableParent != null && !clickableParent.isClickable) { + clickableParent = clickableParent.parent + } + if (clickableParent != null) { + clickableParent.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK) + Log.i(TAG, "Jeeny: Clicked pickup location widget") + jeenyPickupWaitStartTime = 0L // prevent re-clicking immediately + return + } + } + + val editTexts = findAllEditTexts(rootNode) + if (editTexts.isNotEmpty()) { + val pickupField = editTexts.first() // Pickup is usually the first one when both are visible + if (pickupField.text?.toString() != startLoc && !pickupField.text.toString().contains(startLoc)) { + val pickupArgs = android.os.Bundle().apply { + putCharSequence(android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, startLoc) } - destField.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, destArgs) - Log.i(TAG, "Jeeny: Set single field -> $endLoc") + pickupField.performAction(android.view.accessibility.AccessibilityNodeInfo.ACTION_SET_TEXT, pickupArgs) + Log.i(TAG, "Jeeny: Set pickup -> $startLoc") + jeenySearchTypingTime = System.currentTimeMillis() + return + } + } + + if (jeenySearchTypingTime > 0 && System.currentTimeMillis() - jeenySearchTypingTime > 1500) { + val firstWord = startLoc.split(" ").firstOrNull() ?: startLoc + if (clickFirstSuggestion(rootNode, firstWord) || clickFirstGenericSuggestion(rootNode)) { + Log.i(TAG, "Jeeny: Clicked pickup suggestion") + jeenyPickupDone = true + currentState = BotState.READING_PRICE + return } } } @@ -983,7 +1039,15 @@ class ScraperAccessibilityService : AccessibilityService() { } // If not on pickup confirmation screen, read the price - searchPriceByCurrency(rootNode) + val ecoLitePrice = getJeenyEcoLitePrice(rootNode) + if (ecoLitePrice != null && ecoLitePrice > 0) { + Log.i(TAG, "Jeeny: Found Eco lite price: $ecoLitePrice JOD") + submitPriceToServer("$ecoLitePrice JOD") + currentState = BotState.IDLE + } else { + // Fallback to searching any price if eco lite isn't strictly matched + searchPriceByCurrency(rootNode) + } } else -> {} }