Skip to content

Commit 2097bea

Browse files
committed
Refactor SeenTick to use concurrent collections and improve memory management for message views
Signed-off-by: Dev4Mod <wellingtonmods@gmail.com>
1 parent 0305f3b commit 2097bea

File tree

1 file changed

+58
-33
lines changed
  • app/src/main/java/com/wmods/wppenhacer/xposed/features/general

1 file changed

+58
-33
lines changed

app/src/main/java/com/wmods/wppenhacer/xposed/features/general/SeenTick.java

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import android.widget.Toast;
1818

1919
import androidx.annotation.NonNull;
20-
import androidx.collection.ArraySet;
2120

2221
import com.wmods.wppenhacer.xposed.core.Feature;
2322
import com.wmods.wppenhacer.xposed.core.WppCore;
@@ -33,14 +32,16 @@
3332

3433
import org.luckypray.dexkit.query.enums.StringMatchType;
3534

35+
import java.lang.ref.WeakReference;
3636
import java.lang.reflect.Method;
3737
import java.util.ArrayList;
3838
import java.util.Arrays;
3939
import java.util.HashMap;
4040
import java.util.List;
4141
import java.util.Objects;
42+
import java.util.Set;
4243
import java.util.concurrent.CompletableFuture;
43-
import java.util.stream.Collectors;
44+
import java.util.concurrent.ConcurrentHashMap;
4445

4546
import de.robv.android.xposed.XC_MethodHook;
4647
import de.robv.android.xposed.XSharedPreferences;
@@ -49,28 +50,50 @@
4950

5051
public 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

Comments
 (0)