From 1c612ebc8ebbfd753ccf5d404ff21fdf77e2616d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=9ET=CE=9ERNAL?= Date: Sat, 21 Mar 2026 02:09:33 +0530 Subject: [PATCH] fix: force AMOLED theme for newer snap versions --- .../main/assets/lspatch/dexes/metaloader.dex | Bin 6932 -> 11196 bytes .../core/features/impl/ui/CustomTheming.kt | 96 +++++++++++++----- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/app/src/main/assets/lspatch/dexes/metaloader.dex b/app/src/main/assets/lspatch/dexes/metaloader.dex index 21813774350c4726f74926547294cc0d30e03280..47cd34f6cf41f165fa22c8f8ae3defb6e9d1da4c 100644 GIT binary patch literal 11196 zcmbuFeSBP1na7_yGnw}!leA6KY1&SvB}q$@$$KVE3Qg07yfrCJ8YRfWotZnyZD;PC z&PzyfML-~xH(kXFzFpn z#1{V8n||+e&U4N^ug`hTxwn(h7QL+CtglJbMJ$1P}%o>93Fl3!Q-jU#b?Xz zJ$orphKLqtI;$bXRR`TK5Muo(a65?FAzRCc`XNuah~5r4?;-L+Hu;IBHzQt7R2w7; zf>y8x41xpTFjxQ|0*{05fM0>%g8v1|Mxrv{1pyESmw@e{1+;-K&A3O@42d{xQK+Pti4lo9;0a_$c@+_y%|tyaAff$3CEf zTfzO{5%3)NHE?V}TfqQ03^L$W@KJCU{2aJKL=9jZq`(sRBzOWm3tk4l0seaQ9}Iyx zZ~~kG-vX}!f0(ElOn^JV1K>gM7vM$k1Mo6<1^gVm3jQ7ZC-^P+U!ZKon1c$u!27^y@Cf)k_%iqgcpkh6UIDLy{|0LtF@|6O8~{o1PH-Ez z2Ydva0iOk50QM%d32X%&pc`BcCcsr70gi$kxDng|?gr0*=fL;D%ix#be?ZxGq6)AL zbc4&lelQ9S0W4AC1r9SNm@2^(DFk3j1XCbPBVqUnLq+Hgp^1cI36Iha0GB{JK{IFp z5zq?S0Q-gx&U=J7od%+;s2lj&@a0M6!BY=I-7#If! zzyz2C2f>x#5SRkf;4qj0Zv*V3t^%{*YVZzl4fqof13EYgQXmZsz~fZB%4MK+15u?6 zM-dLl@E(LKWOy9mwFoO<8sRlE{0@ZIA#4MD!PSuiB%rT_bb^cwyAfWLVXmVze3R6# zmwGOfpXaLv@O+*?9!?JdZZq7VnAi8qXL*kAE76}S34gjI&*u>4^4Sjf;_^An<#U-F z=5gTq*-nb>WIN&hvYx|F$#U3s_+mZV4Cm)^*mjFt;Hg z!_St4zgrT13E`y7^OF)impLc(|5~Dd9pQPY|2@K*3_CDc#kzP**k0H^`8onPd>`~Y zRzrX<4zum@g(P9W0hHswsQo?SE^sYai!GW~s0zF+D;D5OG<3OP)lmGLhK*+5?u68>At zFH8B1lwXzdZ>9XUkP1C7zAW;!e744xe}rT|WP&~LLY7qMT_Uc~yQO@ulz%4W zy^sU&c$|L#Gk-Uv4KsTJ(hiSW23bZPIu7Y1H{B2E0Z#!h{yq)qgQw+uKIFeq%G-o& zrbndwYawgsX_@|ANIz=ecKA^v_p2OUn#(E2-(Nvi$og4wc@^-~FF{sfrEPK2?XBjKC)3^eaYaJM7G>L=`*6zz7Y(%KY{!dmYxM%meLqSfUs6P)2q3KH}6J z7fIbMQrBN3br(|2P(aqf^XWjl6lFs-?qZB@KpWY1GJz)CD}g973=d)p5T^(w{ z{i@JTaq^fA?Rb+m;xTb?+qn;1PVp+$aaysDPK3i)$(aCKRR#WR;vDu6%J?-jb%;g% zJX4$L898rFp0Ixt1mDj zdU`(Q!1K;7$~)hN88ZUiunxZ?=)7Op%Mf`TQO6z7_Xz!U&JL;{s{elbf&*UgMpA0( zujpDR(2&dFGy+?&LwQ|M*EJ#@bGxWx$xB1-a%bqo7V5=I{&t*pVs1P19;sgky`7rg z9z*JRZH=Wp5Odo~v}-Nx-k7_rL>siUJu$blL|bQRcgEcA5^c!R*2ml)Xlu|;wC(y! zsMi&srW%e#T|Tr7y}`^ZqBp%Rw9fOA)2TGwX|zV^>*aobmH#E5PuWgay%EMsawgP> zeX`MC8n)JioxXq5z5_+!H%IR}7)vz@N9gqG|O3?Yd(4lTwpUCNieWDfIA}*s|w4w*2WC}YE+0jz> zyF~aVyWd`2%Qk-tp6R0Z&@u5g==T_*cG#8QRvPbx_ftLHVpY_i@%ealw4L$U#41_v zInO>mkFh>`^z5bczFyS#8PSdqkBt3s5pG2Z7pM)b@4(4nxi%5+=Det({)|@91TNUUnpkV1y7U1JT745`>EX#wC52k$QbLYVY~fkT^;Pf;Fk}y;aGro$e?luLK}q7 z3fezK)eg4yHV0;J9gpV}Z0sjuMpXyzTU~g+=5;WL9AZ5{yG3ch9Q46MTj8MX2E^-$ z=O=9UQP@$1aa@mcWhUgrd%Y2G!P6T75AI&9ktklPjDQa@_GB;PS@t{Z$-K~eMXn}K zIjrVV#R%|ux9O+Kv=MM46t$Hh&pPz2`ZsIr6bd?R6m-3lg6=wsIv&F)xx@&&5&9eS zluuOLcb+9bUFT3}jk})KdQRQ0P|*7|>b$j%YOqBe3pLQU(Vk;sU-Y`4xbGM~M7hcD z-qt!yqi&>x4$>Z!u?0DryfUBTiF>nq>~w0sAHD9Ou)C6`J=n)WYjI}Ggf6FB(YpcI zj^Aa3_6iwxS76_%LfKn8hLBo=lcU$u{sAXeLoG!=)bw9QU=7!7(`|j|Yc;~$V;Amw zPRf)Q)~c&i6LdJm$PT0Qddgs|!Y->PuH~L|lGojSi`A2!+pM0rN_xV5Shu1NLHnPI z*0ziLs+OaT{FdOyQ+#UlQ}vZb=u)9M7HEZrS4pu~E8B0`64zDQ2juozc2ug3+N=?< zt;)O7AMkl;664IP){Anlfvm@<-2`bz{5ByK;&P?>!?G-J=7!OwbTz;j{GB#Wq8+_fUL%s zz3W8!Mv6l=BK;l_ucr?}I*{*v5#L4+h`2%zi+CfQ5pf4SCgMBjNts?rPub}qJ9gU# zY;?*-583EbHhSDfPuu8DJKb%k57_DBcKWoPp0J}66)()5>*qhzU9tIM7k3xF4>#n) z;;eXb>DT?|#!fCSo*Qdk>|c82N$Yy!q|W6uD^-=vOQ-u!pFVx=hNo7l= zvZ-=*OvH4hzH(8isZ`cgVlL2a0eg%F{PB68<#wqbg5bJk{V?>L-X_x<@I{H^#r6ec`Wi^CyVw@1&9x$LvAuj^ z=RDkQzN|bPT3a7cb2%-ai==WHH6NdkYSFPjf1LTnE6mrHxi*V*R30K zp|qBtHxsmRNYztXB9u2ny0Ks$)k67st(aJQKGD#w52?tK%d19Qqsn1Dk#z{aB!Te$EOZ*Ynu;fd39X$oa)9^Bat=rL`2PGA_MWfzM$uiPtRvHHG!A=m8qG` zpqb8?2F7Sejpxnm@jlwNoF#4=d89-#>BvAPlhWgAUN?;qBWd>0_6y}2IjF{uVnh#E zC;SWYbD^;BduXBvkvE?ipm%A|2~u8+2iC)Ct}el(Ihp37_L2nKt8 zFonsq#+ox6Q5V#RZbtSV&uatOta^O1kk1tIQ+bScx{oTBGYr9q`e@y9e8ecpw(&yh zV#{il6IYfUSk@PFuCX*J)tHOG;!~Px^pVVFDbdR1MhMN|yqd+#^9WRxJS}?JN9$Ls ziX9KHPM9$BLuSE9>|cy)88#ChQ8758ntf&=#{F+B(Jf~kflU<`L3Tecf|ZRK!lKTz z9i1<4B$d+U)YL$BuD}a?xhS^qKgubYqhhl@-)BoF7LaAuB}u}HSI$2igL%Cw=0+N`m@muIMXc@&!=tR8ybEN=Z5ubu zqlL`Mk-JdFKH5~$;uSfjk7r;erBj`?MhLEq7dSQptvHUGOA0CG+P|p93wf(UrNdH8 z7gNaI=KQb}ldR4zuUSLOM-Cqv?;~k0!eb1px%mSsy0}_7rD1MLl~yC+kZP@}#N#*U znaHf3*>B|8)5_IVnqqm0E#qc(j<-QGrzIluVk1#AdQ5C25taSUn`MB{ee(?Qx5|K> z;>}CI9KCslehLJ4+ASsA)%z14PkqoJw9jVHF8W}b#Ol^ zGh~~=UOj`YeTKJa+($>rK7)Cb;TshOKoYutE_`H^wLLbz`h&W|s$s6ab*6O2Agst6&JR&lMBNJ6vM?vd&Gm@aP zcnWKV%Hzdj4f_)mZ)S8YVf8CFnM~rCN$cW;Y!-XR$|Djf30iOM`YqP3-!gOHP{GLS zY1Ed`P&ND9L2ep_R>mijlM6V*iRCKC(9VP4#%+*0NC6JlM^C~Gxawd7>$&rT-s8-c3nBatpHreOUmulQr9nq))I+0h7S^)8V zqWxMz%sEw+Xk>fcPzmoO9xpkgkP|5|2XaC3{wM;X1zgNQoFdX1jD`Dx#tT(`o_rk0 zn%LU%$3^6jmNN@k7}Of16e9yU>%2r>R?5NVuspw!2VrZCl6PM0Mq?NWa?Pu`35*jd zI=OVwDb%eer`i9L)7n_btHX}vsobaM4rcWxt&J&W>a(rY6&rcomyM6tE(#+?db05)RM_$TU71sZtv)hcDBa4I+AT&on5`1ZM|*e z;6+Zg*mhTrhSk9wRb;UQ^sF{v8WV+7iWJOgP7X*pR_^(O0C{ro(KBkQpdl#iYM-8^ zvK(Gk^5n!I=L=Xhxk4-_PD

6<~_ioGN*0%g^h%kTr-Qg!mm$HJ0DuLS|CD>&?Mf zjF7Mo^5xCtqdvLwrsb5$k!L^SDH!rFL6ytFE3`b;1$hg(;(G|W7P!fiNm{$0XY*L@ z*6J&Irocj)EhJmOxzIFlvLNSzIMPtr^;sPoj(suOO-|gdg>V0+a^sDMqVEjHFsJbQ z;ay?4l<>|lyFfi9PL$1QGoc|NtKpR(ry&swrx^|@el%A|=fb;^@HpDeFwC_X=Zx^K z_UO)VW=_sH9{m5#Icj7BvRaYEq(T8>%%OWIJ=)UN(bCoveiwfRQ1(^*5+4v1^4j*_ ze3RoY{!C52TNLGD|Gswdq>DTolyeP8l zSc@!7^(eA@Y?IAqr#7$Rjo`sCXrA4qcz*(3-^3qaI_?imoVdwxVzUz^b#73W|0bcm zH>|u@d3du8A3%0(P&`*c^`iy_Wt?hMJWn->Z|q_S_=bzWvHSvL0DmVL1^nAr{(jO9 z`1h~;9gzR8ntub!Uzgtw_;;{QNH5^u!YU!_H)8yISUbY}n;-svVFe#)@gp!*8 literal 6932 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 cpWox= 2 } ?: return + + requestedAttrs.forEachIndexed { index, attrId -> + val offset = index * stride + if (offset + 1 >= typedArrayData.size) return@forEachIndexed + + val type = typedArrayData[offset] + if (type !in colorTypes) return@forEachIndexed + + val originalColor = runCatching { result.getColor(index, Int.MIN_VALUE) }.getOrNull() + ?.takeIf { it != Int.MIN_VALUE } + ?: return@forEachIndexed + + val attrName = runCatching { context.androidContext.resources.getResourceEntryName(attrId) }.getOrNull() + val shouldPatch = attrId in patchedAttrIds || shouldPatch(attrId, attrName, originalColor) + if (!shouldPatch) return@forEachIndexed + + typedArrayData[offset + 1] = amoledBlack + if (patchedAttrIds.add(attrId)) { + context.log.verbose( + "[AMOLED PATCH] Patched attrId 0x${attrId.toString(16)} (${attrName ?: "unknown"}) from 0x${originalColor.toUInt().toString(16)} to AMOLED black" + ) + } + } + } + + private fun patchProgrammaticColor(color: Int): Int { + return if (isNearBlackOpaque(color) && color != amoledBlack) amoledBlack else color + } + override fun init() { if (!context.config.userInterface.forceAmoledTheme.get()) return onNextActivityCreate { context.androidContext.theme.javaClass - .getMethod("obtainStyledAttributes", IntArray::class.java) - .hook(HookStage.AFTER) { param -> - val array = param.arg(0) - val attrId = array[0] - val result = param.getResult() as TypedArray - val type = result.getType(0) - if (type !in colorTypes) return@hook - - val originalColor = runCatching { result.getColor(0, Int.MIN_VALUE) }.getOrNull() - ?.takeIf { it != Int.MIN_VALUE } - ?: return@hook - - val attrName = runCatching { context.androidContext.resources.getResourceEntryName(attrId) }.getOrNull() - val shouldPatch = attrId in patchedAttrIds || shouldPatch(attrId, attrName, originalColor) - if (!shouldPatch) return@hook - - val typedArrayData = runCatching { result.getObjectField("mData") as IntArray }.getOrNull() ?: return@hook - if (typedArrayData.size < 2) return@hook - - typedArrayData[1] = amoledBlack - if (patchedAttrIds.add(attrId)) { - context.log.verbose( - "[AMOLED PATCH] Patched attrId 0x${attrId.toString(16)} (${attrName ?: "unknown"}) from 0x${originalColor.toUInt().toString(16)} to AMOLED black" - ) - } + .hook("obtainStyledAttributes", HookStage.AFTER) { param -> + val requestedAttrs = param.args().firstOrNull { it is IntArray } as? IntArray + val result = param.getResult() as? TypedArray ?: return@hook + patchTypedArray(result, requestedAttrs) } + + context.androidContext.javaClass + .hook("obtainStyledAttributes", HookStage.AFTER) { param -> + val requestedAttrs = param.args().firstOrNull { it is IntArray } as? IntArray + val result = param.getResult() as? TypedArray ?: return@hook + patchTypedArray(result, requestedAttrs) + } + + View::class.java.hook("setBackgroundColor", HookStage.BEFORE) { param -> + val color = param.argNullable(0) ?: return@hook + val patched = patchProgrammaticColor(color) + if (patched != color) { + param.setArg(0, patched) + } + } + + ColorDrawable::class.java.hookConstructor(HookStage.BEFORE) { param -> + val color = param.argNullable(0) ?: return@hookConstructor + val patched = patchProgrammaticColor(color) + if (patched != color) { + param.setArg(0, patched) + } + } + + ColorDrawable::class.java.hook("setColor", HookStage.BEFORE) { param -> + val color = param.argNullable(0) ?: return@hook + val patched = patchProgrammaticColor(color) + if (patched != color) { + param.setArg(0, patched) + } + } } } }