Skip to content

Commit 0a472ad

Browse files
committed
refactor download hooks
1 parent df2467f commit 0a472ad

File tree

9 files changed

+104
-212
lines changed

9 files changed

+104
-212
lines changed

.idea/other.xml

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/java/com/wmods/wppenhacer/xposed/core/components/FMessageWpp.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.wmods.wppenhacer.xposed.core.devkit.Unobfuscator;
44
import com.wmods.wppenhacer.xposed.utils.ReflectionUtils;
55

6+
import java.io.File;
67
import java.lang.reflect.Field;
78
import java.lang.reflect.Method;
89

@@ -21,6 +22,7 @@ public class FMessageWpp {
2122
private static Method messageWithMediaMethod;
2223
private static Field mediaTypeField;
2324
private static Method getOriginalMessageKey;
25+
private static Class abstractMediaMessageClass;
2426
private final Object fmessage;
2527

2628
public FMessageWpp(Object fMessage) {
@@ -47,6 +49,7 @@ public static void init(ClassLoader classLoader) throws Exception {
4749
deviceJidMethod = ReflectionUtils.findMethodUsingFilter(TYPE, method -> method.getReturnType().equals(XposedHelpers.findClass("com.whatsapp.jid.DeviceJid", classLoader)));
4850
mediaTypeField = Unobfuscator.loadMediaTypeField(classLoader);
4951
getOriginalMessageKey = Unobfuscator.loadOriginalMessageKey(classLoader);
52+
abstractMediaMessageClass = Unobfuscator.loadAbstractMediaMessageClass(classLoader);
5053
}
5154

5255
public Object getUserJid() {
@@ -110,6 +113,31 @@ public String getMessageStr() {
110113
}
111114
}
112115

116+
public boolean isMediaFile() {
117+
try {
118+
return abstractMediaMessageClass.isInstance(fmessage);
119+
} catch (Exception e) {
120+
return false;
121+
}
122+
}
123+
124+
public File getMediaFile() {
125+
try {
126+
if (!isMediaFile()) return null;
127+
for (var field : abstractMediaMessageClass.getDeclaredFields()) {
128+
if (field.getType().isPrimitive()) continue;
129+
var fileField = ReflectionUtils.getFieldByType(field.getType(), File.class);
130+
if (fileField != null) {
131+
var mediaFile = ReflectionUtils.getField(field, fmessage);
132+
return (File) fileField.get(mediaFile);
133+
}
134+
}
135+
} catch (Exception e) {
136+
XposedBridge.log(e);
137+
}
138+
return null;
139+
}
140+
113141

114142
public int getMediaType() {
115143
try {

app/src/main/java/com/wmods/wppenhacer/xposed/core/devkit/Unobfuscator.java

Lines changed: 17 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.luckypray.dexkit.result.UsingFieldData;
3232
import org.luckypray.dexkit.util.DexSignUtil;
3333

34-
import java.io.File;
3534
import java.lang.reflect.Constructor;
3635
import java.lang.reflect.Field;
3736
import java.lang.reflect.Method;
@@ -517,11 +516,14 @@ public synchronized static Method loadStatusActivePage(ClassLoader classLoader)
517516
});
518517
}
519518

520-
public synchronized static Class<?> loadStatusDownloadMediaClass(ClassLoader classLoader) throws Exception {
519+
520+
public synchronized static Class<?> loadMenuManagerClass(ClassLoader classLoader) throws Exception {
521521
return UnobfuscatorCache.getInstance().getClass(classLoader, () -> {
522-
var clazz = findFirstClassUsingStrings(classLoader, StringMatchType.Contains, "static.whatsapp.net/downloadable?category=PSA");
523-
if (clazz == null) throw new Exception("StatusDownloadMedia class not found");
524-
return clazz;
522+
var methods = findAllMethodUsingStrings(classLoader, StringMatchType.Contains, "MenuPopupHelper cannot be used without an anchor");
523+
for (var method : methods) {
524+
if (method.getReturnType() == void.class) return method.getDeclaringClass();
525+
}
526+
throw new Exception("MenuManager class not found");
525527
});
526528
}
527529

@@ -534,43 +536,6 @@ public synchronized static Method loadMenuStatusMethod(ClassLoader loader) throw
534536
});
535537
}
536538

537-
public synchronized static Field loadStatusDownloadFileField(ClassLoader classLoader) throws Exception {
538-
return UnobfuscatorCache.getInstance().getField(classLoader, () -> {
539-
var clazz = loadStatusDownloadMediaClass(classLoader);
540-
for (Field clazzField : clazz.getFields()) {
541-
var clazz2 = clazzField.getType();
542-
var field = ReflectionUtils.getFieldByType(clazz2, File.class);
543-
if (field != null) return field;
544-
}
545-
546-
throw new Exception("StatusDownloadFile field not found");
547-
});
548-
}
549-
550-
public synchronized static Class<?> loadStatusDownloadSubMenuClass(ClassLoader classLoader) throws Exception {
551-
return UnobfuscatorCache.getInstance().getClass(classLoader, () -> {
552-
var classes = dexkit.findClass(
553-
new FindClass().matcher(
554-
new ClassMatcher().addMethod(
555-
new MethodMatcher()
556-
.addUsingString("MenuPopupHelper", StringMatchType.Contains)
557-
.returnType(void.class)
558-
)
559-
)
560-
);
561-
if (classes.isEmpty()) throw new Exception("StatusDownloadSubMenu method not found");
562-
return classes.get(0).getInstance(classLoader);
563-
});
564-
}
565-
566-
public synchronized static Class<?> loadStatusDownloadMenuClass(ClassLoader classLoader) throws Exception {
567-
return UnobfuscatorCache.getInstance().getClass(classLoader, () -> {
568-
var clazz = findFirstClassUsingStrings(classLoader, StringMatchType.Contains, "android:menu:expandedactionview");
569-
if (clazz == null) throw new Exception("StatusDownloadMenu class not found");
570-
return clazz;
571-
});
572-
}
573-
574539
// TODO: Classes and methods to ViewOnce
575540

576541
public synchronized static Method[] loadViewOnceMethod(ClassLoader classLoader) throws Exception {
@@ -631,53 +596,6 @@ public synchronized static Method loadViewOnceDownloadMenuMethod(ClassLoader cla
631596
});
632597
}
633598

634-
public synchronized static Field loadViewOnceDownloadMenuField(ClassLoader classLoader) throws Exception {
635-
return UnobfuscatorCache.getInstance().getField(classLoader, () -> {
636-
var method = loadViewOnceDownloadMenuMethod(classLoader);
637-
var clazz = XposedHelpers.findClass("com.whatsapp.mediaview.MediaViewFragment", classLoader);
638-
var methodData = dexkit.getMethodData(method);
639-
var fields = methodData.getUsingFields();
640-
for (UsingFieldData field : fields) {
641-
Field field1 = field.getField().getFieldInstance(classLoader);
642-
if (field1.getType() == int.class && field1.getDeclaringClass() == clazz) {
643-
return field1;
644-
}
645-
}
646-
throw new Exception("ViewOnceDownloadMenu field not found");
647-
});
648-
}
649-
650-
public synchronized static Field loadViewOnceDownloadMenuField2(ClassLoader classLoader) throws Exception {
651-
return UnobfuscatorCache.getInstance().getField(classLoader, () -> {
652-
var methodData = dexkit.findMethod(new FindMethod().matcher(new MethodMatcher().addUsingString("photo_progress_fragment"))).get(0);
653-
var clazz = methodData.getMethodInstance(classLoader).getDeclaringClass();
654-
var fields = methodData.getUsingFields();
655-
for (UsingFieldData field : fields) {
656-
Field field1 = field.getField().getFieldInstance(classLoader);
657-
if (field1.getType() == int.class && field1.getDeclaringClass() == clazz) {
658-
return field1;
659-
}
660-
}
661-
throw new Exception("ViewOnceDownloadMenu field 2 not found");
662-
});
663-
}
664-
665-
/**
666-
* @noinspection SimplifyOptionalCallChains
667-
*/
668-
public synchronized static Method loadViewOnceDownloadMenuCallMethod(ClassLoader loader) throws Exception {
669-
return UnobfuscatorCache.getInstance().getMethod(loader, () -> {
670-
var clazz = XposedHelpers.findClass("com.whatsapp.mediaview.MediaViewFragment", loader);
671-
var method = Arrays.stream(clazz.getDeclaredMethods()).filter(m ->
672-
((m.getParameterCount() == 2 && Objects.equals(m.getParameterTypes()[1], int.class) && Objects.equals(m.getParameterTypes()[0], clazz))
673-
|| (m.getParameterCount() == 1 && Objects.equals(m.getParameterTypes()[0], int.class))) &&
674-
Modifier.isPublic(m.getModifiers()) && Object.class.isAssignableFrom(m.getReturnType())
675-
).findFirst();
676-
if (!method.isPresent())
677-
throw new Exception("ViewOnceDownloadMenuCall method not found");
678-
return method.get();
679-
});
680-
}
681599

682600
// TODO: Methods and Classes for Change Colors
683601

@@ -1778,4 +1696,14 @@ public static synchronized Class loadCachedMessageStore(ClassLoader loader) thro
17781696
return cacheMsClass;
17791697
});
17801698
}
1699+
1700+
public static synchronized Class loadAbstractMediaMessageClass(ClassLoader loader) throws Exception {
1701+
return UnobfuscatorCache.getInstance().getClass(loader, () -> {
1702+
var fMessageClass = findFirstClassUsingStrings(loader, StringMatchType.Contains, "static.whatsapp.net/downloadable?category=PSA");
1703+
if (fMessageClass == null)
1704+
throw new RuntimeException("AbstractMediaMessage class not found");
1705+
return fMessageClass;
1706+
});
1707+
}
1708+
17811709
}

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

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
import com.wmods.wppenhacer.xposed.core.devkit.Unobfuscator;
1111
import com.wmods.wppenhacer.xposed.utils.ReflectionUtils;
1212

13-
import java.lang.reflect.Field;
13+
import java.util.Arrays;
1414
import java.util.HashSet;
1515
import java.util.List;
16+
import java.util.Objects;
17+
import java.util.stream.Collectors;
1618

1719
import de.robv.android.xposed.XC_MethodHook;
1820
import de.robv.android.xposed.XSharedPreferences;
@@ -30,19 +32,9 @@ public MenuStatus(@NonNull ClassLoader classLoader, @NonNull XSharedPreferences
3032
@Override
3133
public void doHook() throws Throwable {
3234

33-
var mediaClass = Unobfuscator.loadStatusDownloadMediaClass(classLoader);
34-
logDebug("Media class: " + mediaClass.getName());
35-
// var menuStatusClass = Unobfuscator.loadMenuStatusClass(classLoader);
3635
var menuStatusMethod = Unobfuscator.loadMenuStatusMethod(classLoader);
3736
logDebug("MenuStatus method: " + menuStatusMethod.getName());
38-
var fieldFile = Unobfuscator.loadStatusDownloadFileField(classLoader);
39-
logDebug("File field: " + fieldFile.getName());
40-
var clazzSubMenu = Unobfuscator.loadStatusDownloadSubMenuClass(classLoader);
41-
logDebug("SubMenu class: " + clazzSubMenu.getName());
42-
var clazzMenu = Unobfuscator.loadStatusDownloadMenuClass(classLoader);
43-
logDebug("Menu class: " + clazzMenu.getName());
44-
var menuField = ReflectionUtils.getFieldByType(clazzSubMenu, clazzMenu);
45-
logDebug("Menu field: " + menuField.getName());
37+
var menuManagerClass = Unobfuscator.loadMenuManagerClass(classLoader);
4638

4739
Class<?> StatusPlaybackBaseFragmentClass = classLoader.loadClass("com.whatsapp.status.playback.fragment.StatusPlaybackBaseFragment");
4840
Class<?> StatusPlaybackContactFragmentClass = classLoader.loadClass("com.whatsapp.status.playback.fragment.StatusPlaybackContactFragment");
@@ -51,23 +43,11 @@ public void doHook() throws Throwable {
5143
XposedBridge.hookMethod(menuStatusMethod, new XC_MethodHook() {
5244
@Override
5345
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
54-
Object fragmentInstance;
55-
Menu menu;
56-
if (param.args[0] instanceof Menu) {
57-
menu = (Menu) param.args[0];
58-
fragmentInstance = param.thisObject;
59-
} else {
60-
var clazz = param.thisObject.getClass();
61-
Field subMenuField = ReflectionUtils.findFieldUsingFilter(clazz, f -> f.getType() == Object.class && clazzSubMenu.isInstance(ReflectionUtils.getField(f, param.thisObject)));
62-
Object subMenu = ReflectionUtils.getField(subMenuField, param.thisObject);
63-
menu = (Menu) ReflectionUtils.getField(menuField, subMenu);
64-
var fragment = ReflectionUtils.findFieldUsingFilter(clazz, f -> StatusPlaybackBaseFragmentClass.isInstance(ReflectionUtils.getField(f, param.thisObject)));
65-
if (fragment == null) {
66-
logDebug("Fragment not found");
67-
return;
68-
}
69-
fragmentInstance = fragment.get(param.thisObject);
70-
}
46+
var fieldObjects = Arrays.stream(param.method.getDeclaringClass().getDeclaredFields()).map(field -> ReflectionUtils.getField(field, param.thisObject)).filter(Objects::nonNull).collect(Collectors.toList());
47+
var menuManager = fieldObjects.stream().filter(menuManagerClass::isInstance).findFirst().orElse(null);
48+
var menuField = ReflectionUtils.getFieldByExtendType(menuManagerClass, Menu.class);
49+
var menu = (Menu) ReflectionUtils.getField(menuField, menuManager);
50+
var fragmentInstance = fieldObjects.stream().filter(StatusPlaybackBaseFragmentClass::isInstance).findFirst().orElse(null);
7151

7252
var index = (int) XposedHelpers.getObjectField(fragmentInstance, "A00");
7353
var listStatus = (List) listStatusField.get(fragmentInstance);

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

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -280,28 +280,25 @@ public void onClick(MenuItem item, Object fragmentInstance, FMessageWpp fMessage
280280
private void hookViewOnceScreen(int ticktype) throws Exception {
281281
var menuMethod = Unobfuscator.loadViewOnceDownloadMenuMethod(classLoader);
282282
logDebug(Unobfuscator.getMethodDescriptor(menuMethod));
283-
var menuIntField = Unobfuscator.loadViewOnceDownloadMenuField(classLoader);
284-
logDebug(Unobfuscator.getFieldDescriptor(menuIntField));
285-
var classThreadMessage = Unobfuscator.loadFMessageClass(classLoader);
286283

287284
XposedBridge.hookMethod(menuMethod, new XC_MethodHook() {
288285
@Override
289286
@SuppressLint("DiscouragedApi")
290287
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
291-
var id = XposedHelpers.getIntField(param.thisObject, menuIntField.getName());
292-
if (id == 3 || id == 0) {
293-
Menu menu = (Menu) param.args[0];
294-
MenuItem item = menu.add(0, 0, 0, ResId.string.send_blue_tick).setIcon(Utils.getID("ic_notif_mark_read", "drawable"));
295-
if (ticktype == 1) item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
296-
item.setOnMenuItemClickListener(item1 -> {
297-
var messageField = ReflectionUtils.getFieldByExtendType(menuMethod.getDeclaringClass(), classThreadMessage);
298-
var messageObject = XposedHelpers.getObjectField(param.thisObject, messageField.getName());
299-
sendBlueTickMedia(messageObject, true);
300-
Utils.showToast(Utils.getApplication().getString(ResId.string.sending_read_blue_tick), Toast.LENGTH_SHORT);
301-
return true;
302-
});
303-
}
304-
288+
var messageField = ReflectionUtils.getFieldByExtendType(menuMethod.getDeclaringClass(), FMessageWpp.TYPE);
289+
if (messageField == null) return;
290+
var fMessage = new FMessageWpp(messageField.get(param.thisObject));
291+
var id = fMessage.getMediaType();
292+
// check media is view once
293+
if (id != 42 && id != 43) return;
294+
Menu menu = (Menu) param.args[0];
295+
MenuItem item = menu.add(0, 0, 0, ResId.string.send_blue_tick).setIcon(Utils.getID("ic_notif_mark_read", "drawable"));
296+
if (ticktype == 1) item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
297+
item.setOnMenuItemClickListener(item1 -> {
298+
sendBlueTickMedia(fMessage.getObject(), true);
299+
Utils.showToast(Utils.getApplication().getString(ResId.string.sending_read_blue_tick), Toast.LENGTH_SHORT);
300+
return true;
301+
});
305302
}
306303
});
307304

0 commit comments

Comments
 (0)