1717import android .widget .Toast ;
1818
1919import androidx .annotation .NonNull ;
20- import androidx .collection .ArraySet ;
2120
2221import com .wmods .wppenhacer .xposed .core .Feature ;
2322import com .wmods .wppenhacer .xposed .core .WppCore ;
3332
3433import org .luckypray .dexkit .query .enums .StringMatchType ;
3534
35+ import java .lang .ref .WeakReference ;
3636import java .lang .reflect .Method ;
3737import java .util .ArrayList ;
3838import java .util .Arrays ;
3939import java .util .HashMap ;
4040import java .util .List ;
4141import java .util .Objects ;
42+ import java .util .Set ;
4243import java .util .concurrent .CompletableFuture ;
43- import java .util .stream . Collectors ;
44+ import java .util .concurrent . ConcurrentHashMap ;
4445
4546import de .robv .android .xposed .XC_MethodHook ;
4647import de .robv .android .xposed .XSharedPreferences ;
4950
5051public class SeenTick extends Feature {
5152
52- private static final ArraySet <FMessageWpp > statuses = new ArraySet <> ();
53+ private final Set <FMessageWpp > statuses = ConcurrentHashMap . newKeySet ();
5354 private static Object mWaJobManager ;
5455 private static Class <?> mSendReadClass ;
5556 private static Method WaJobManagerMethod ;
5657 private static FMessageWpp .UserJid currentJid ;
5758 private static String currentScreen = "none" ;
58- private static final HashMap <String , ImageView > messageMap = new HashMap <>();
59+ private final ConcurrentHashMap <String , WeakReference < ImageView >> messageMap = new ConcurrentHashMap <>();
5960
6061 public SeenTick (@ NonNull ClassLoader loader , @ NonNull XSharedPreferences preferences ) {
6162 super (loader , preferences );
6263 }
6364
6465 public static void setSeenButton (ImageView buttonImage , boolean b ) {
6566 Drawable originalDrawable = DesignUtils .getDrawableByName ("ic_notif_mark_read" );
67+ if (originalDrawable == null ) {
68+ buttonImage .setImageResource (Utils .getID ("ic_notif_mark_read" , "drawable" ));
69+ if (b ) buttonImage .setColorFilter (Color .CYAN , PorterDuff .Mode .SRC_ATOP );
70+ return ;
71+ }
72+
6673 Drawable clonedDrawable ;
6774
6875 if (originalDrawable instanceof BitmapDrawable bitmapDrawable ) {
6976 Bitmap bitmap = bitmapDrawable .getBitmap ();
70- Bitmap clonedBitmap = bitmap .copy (bitmap .getConfig (), true );
77+ Bitmap .Config config = bitmap .getConfig () != null ? bitmap .getConfig () : Bitmap .Config .ARGB_8888 ;
78+ Bitmap clonedBitmap ;
79+ try {
80+ clonedBitmap = bitmap .copy (config , true );
81+ } catch (Exception ex ) {
82+ clonedBitmap = Bitmap .createBitmap (bitmap .getWidth (), bitmap .getHeight (), Bitmap .Config .ARGB_8888 );
83+ try {
84+ android .graphics .Canvas canvas = new android .graphics .Canvas (clonedBitmap );
85+ canvas .drawBitmap (bitmap , 0f , 0f , null );
86+ } catch (Exception ignore ) {
87+ }
88+ }
7189 clonedDrawable = new BitmapDrawable (buttonImage .getResources (), clonedBitmap );
7290 } else {
73- clonedDrawable = Objects .requireNonNull (originalDrawable .getConstantState ()).newDrawable ().mutate ();
91+ var cs = originalDrawable .getConstantState ();
92+ if (cs != null ) {
93+ clonedDrawable = cs .newDrawable ().mutate ();
94+ } else {
95+ clonedDrawable = originalDrawable .mutate ();
96+ }
7497 }
7598 if (b ) {
7699 clonedDrawable .setColorFilter (Color .CYAN , PorterDuff .Mode .SRC_ATOP );
@@ -79,6 +102,16 @@ public static void setSeenButton(ImageView buttonImage, boolean b) {
79102 buttonImage .postInvalidate ();
80103 }
81104
105+ private void registerMessageView (String messageId , ImageView view ) {
106+ if (messageId == null || view == null ) return ;
107+ messageMap .put (messageId , new WeakReference <>(view ));
108+ }
109+
110+ private ImageView getRegisteredView (String messageId ) {
111+ WeakReference <ImageView > ref = messageMap .get (messageId );
112+ return ref == null ? null : ref .get ();
113+ }
114+
82115 @ Override
83116 public void doHook () throws Throwable {
84117
@@ -195,15 +228,15 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
195228 buttonImage .setBackground (border );
196229 contentView .setOrientation (LinearLayout .HORIZONTAL );
197230 contentView .addView (buttonImage , 0 );
198- messageMap . put (key .messageID , buttonImage );
231+ registerMessageView (key .messageID , buttonImage );
199232 buttonImage .setOnClickListener (v -> CompletableFuture .runAsync (() -> {
200233 Utils .showToast (view .getContext ().getString (ResId .string .sending_read_blue_tick ), Toast .LENGTH_SHORT );
201234 sendBlueTickStatus (currentJid );
202- setSeenButton (buttonImage , true );
235+ buttonImage . post (() -> setSeenButton (buttonImage , true ) );
203236 }));
204237 CompletableFuture .runAsync (() -> {
205238 var seen = MessageStore .getInstance ().isReadMessageStatus (key .messageID );
206- setSeenButton (buttonImage , seen );
239+ buttonImage . post (() -> setSeenButton (buttonImage , seen ) );
207240 });
208241 }
209242 });
@@ -272,7 +305,7 @@ public void onClick(MenuItem item, Object fragmentInstance, FMessageWpp fMessage
272305 if (!fMessage .getKey ().isFromMe ) {
273306 statuses .add (fMessage );
274307 }
275- var view = messageMap . get (messageId );
308+ var view = getRegisteredView (messageId );
276309 if (view != null ) {
277310 view .post (() -> setSeenButton (view , true ));
278311 }
@@ -372,8 +405,10 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
372405
373406 if (Objects .equals (currentScreen , "status" ) && !userJid .isStatus ()) {
374407 if (statuses .isEmpty ()) return ;
375- MessageStore .getInstance ().storeMessageRead (statuses .valueAt (0 ).getKey ().messageID );
376- var view = messageMap .get (statuses .valueAt (0 ).getKey ().messageID );
408+ var first = statuses .stream ().findFirst ().orElse (null );
409+ if (first == null ) return ;
410+ MessageStore .getInstance ().storeMessageRead (first .getKey ().messageID );
411+ var view = getRegisteredView (first .getKey ().messageID );
377412 if (view != null ) view .post (() -> setSeenButton (view , true ));
378413 sendBlueTickStatus (currentJid );
379414 } else {
@@ -405,9 +440,8 @@ private void sendBlueTick(FMessageWpp.UserJid userJid) {
405440 return ;
406441
407442 if (prefs .getBoolean ("hideaudioseen" , false )) {
408- var audioMessages = messages .stream ().filter (fMessageWpp -> fMessageWpp .getMediaType () == 2 ).collect (Collectors .toList ());
409- for (var audioMessage : audioMessages ) {
410- sendBlueTickMedia (audioMessage );
443+ for (var m : messages ) {
444+ if (m .getMediaType () == 2 ) sendBlueTickMedia (m );
411445 }
412446 }
413447 sendBlueTickMsg (userJid , messages );
@@ -419,13 +453,13 @@ private void sendBlueTickMsg(FMessageWpp.UserJid userJid, ArrayList<FMessageWpp>
419453 if (messages .isEmpty ())
420454 return ;
421455 try {
422- HashMap <FMessageWpp .UserJid , List <String >> messageMap = new HashMap <>();
456+ HashMap <FMessageWpp .UserJid , List <String >> groupedMap = new HashMap <>();
423457 for (FMessageWpp message : messages ) {
424458 var userJidMsg = userJid .isGroup () ? message .getUserJid () : message .getKey ().remoteJid ;
425- messageMap .computeIfAbsent (userJidMsg , k -> new ArrayList <>()).add (message .getKey ().messageID );
459+ groupedMap .computeIfAbsent (userJidMsg , k -> new ArrayList <>()).add (message .getKey ().messageID );
426460 }
427461
428- for (var entry : messageMap .entrySet ()) {
462+ for (var entry : groupedMap .entrySet ()) {
429463 var userJidMsg = entry .getKey ();
430464 String [] messageIds = entry .getValue ().toArray (new String [0 ]);
431465 var participant = userJid .isGroup () ? userJidMsg .userJid : null ;
@@ -436,37 +470,28 @@ private void sendBlueTickMsg(FMessageWpp.UserJid userJid, ArrayList<FMessageWpp>
436470 );
437471 XposedHelpers .setAdditionalInstanceField (sendJob , "blue_on_reply" , true );
438472 WaJobManagerMethod .invoke (mWaJobManager , sendJob );
439-
440- Object sendJob2 = XposedHelpers .newInstance (
441- mSendReadClass , userJid .phoneJid , participant , null , null , messageIds , -1 , 1L , false
442- );
443- XposedHelpers .setAdditionalInstanceField (sendJob2 , "blue_on_reply" , true );
444- WaJobManagerMethod .invoke (mWaJobManager , sendJob2 );
445473 }
446- } catch (Throwable e ) {
474+ } catch (Exception e ) {
447475 logDebug (e );
448476 }
449477 }
450478
451479 private void sendBlueTickStatus (FMessageWpp .UserJid currentJid ) {
452480 CompletableFuture .runAsync (() -> {
453- if (statuses .isEmpty () || currentJid == null || "status_me" .equals (currentJid )) return ;
481+ if (statuses .isEmpty () || currentJid == null || "status_me" .equals (currentJid .getPhoneNumber ()))
482+ return ;
454483 try {
455484 var arr_s = statuses .stream ().map (item -> item .getKey ().messageID ).toArray (String []::new );
456485 Arrays .stream (arr_s ).forEach (s -> MessageStore .getInstance ().storeMessageRead (s ));
457486 var userJidSender = WppCore .createUserJid ("status@broadcast" );
458487
459- var sendJob = XposedHelpers .newInstance (mSendReadClass , userJidSender , currentJid .phoneJid , null , null , arr_s , -1 , 0L , false );
460- XposedHelpers .setAdditionalInstanceField (sendJob , "blue_on_reply" , true );
461- WaJobManagerMethod .invoke (mWaJobManager , sendJob );
462-
463- var sendJob2 = XposedHelpers .newInstance (mSendReadClass , userJidSender , currentJid .userJid , null , null , arr_s , -1 , 0L , false );
488+ var sendJob2 = XposedHelpers .newInstance (mSendReadClass , userJidSender , currentJid .phoneJid , null , null , arr_s , -1 , 0L , false );
464489 XposedHelpers .setAdditionalInstanceField (sendJob2 , "blue_on_reply" , true );
465490 WaJobManagerMethod .invoke (mWaJobManager , sendJob2 );
466491
467492 statuses .clear ();
468- } catch (Throwable e ) {
469- XposedBridge . log ( "Error: " + e . getMessage () );
493+ } catch (Exception e ) {
494+ logDebug ( e );
470495 }
471496 }, Utils .getExecutor ());
472497 }
0 commit comments