Skip to content

Commit

Permalink
improve custom Privacy
Browse files Browse the repository at this point in the history
  • Loading branch information
Dev4Mod committed Oct 14, 2024
1 parent 970c9dd commit bc425d8
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,12 @@ public static String getCurrentRawJID() {

public static String stripJID(String str) {
try {
return (str.contains("@g.us") || str.contains("@s.whatsapp.net") || str.contains("@broadcast")) ? str.substring(0, str.indexOf("@")) : str;
if (str.contains(".") && str.contains("@") && str.indexOf(".") < str.indexOf("@")) {
return str.substring(0, str.indexOf("."));
} else if (str.contains("@g.us") || str.contains("@s.whatsapp.net") || str.contains("@broadcast")) {
return str.substring(0, str.indexOf("@"));
}
return str;
} catch (Exception e) {
XposedBridge.log(e.getMessage());
return str;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
Expand Down Expand Up @@ -53,6 +54,7 @@

public class Unobfuscator {

private static final String TAG = "Unobfuscator";
private static DexKitBridge dexkit;

public static final HashMap<String, Object> cache = new HashMap<>();
Expand Down Expand Up @@ -869,7 +871,8 @@ public synchronized static Method loadOnChangeStatus(ClassLoader loader) throws
// for 19.xx, the current implementation returns wrong method
if (method.getParameterCount() < 6) {
ClassData declaringClassData = dexkit.getClassData(method.getDeclaringClass());
if (declaringClassData == null) throw new Exception("OnChangeStatus method not found");
if (declaringClassData == null)
throw new Exception("OnChangeStatus method not found");

Class<?> arg1Class = findFirstClassUsingStrings(loader, StringMatchType.Contains, "problematic contact:");
MethodDataList methodData = declaringClassData.findMethod(
Expand Down Expand Up @@ -1733,4 +1736,18 @@ public static Method loadTranscribeMethod(ClassLoader classLoader) throws Except
public static Method loadCheckSupportLanguage(ClassLoader classLoader) throws Exception {
return UnobfuscatorCache.getInstance().getMethod(classLoader, () -> findFirstMethodUsingStrings(classLoader, StringMatchType.Equals, "Unsupported language"));
}

public static Class loadUnkTranscript(ClassLoader classLoader) throws Exception {
return UnobfuscatorCache.getInstance().getClass(classLoader, () -> {
var loadTranscribe = loadTranscribeMethod(classLoader);
var callbackClass = loadTranscribe.getParameterTypes()[1];
var onComplete = ReflectionUtils.findMethodUsingFilter(callbackClass, method -> method.getParameterCount() == 4);
var resultTypeClass = onComplete.getParameterTypes()[0].getName();
Log.i(TAG, resultTypeClass);
var classDataList = dexkit.findClass(FindClass.create().matcher(ClassMatcher.create().addUsingString("Unknown").superClass(resultTypeClass)));
if (classDataList.isEmpty())
throw new RuntimeException("UnkTranscript class not found");
return classDataList.get(0).getInstance(classLoader);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,18 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}

public boolean checkCallBlock(Object userJid, int type) throws IllegalAccessException, InvocationTargetException {
if (type == 0) return false;
if (type == 1) return true;

var jid = WppCore.stripJID(WppCore.getRawString(userJid));
if (jid == null) return false;
var customprivacy = CustomPrivacy.getJSON(jid);

if (type == 1) {
return customprivacy.optBoolean("BlockCall", true);
}

if (type == 0) {
return customprivacy.optBoolean("BlockCall", false);
}

switch (type) {
case 2:
if (WppCore.stripJID(jid).equals(jid)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
Expand All @@ -20,7 +21,9 @@
import org.json.JSONObject;

import java.lang.reflect.Method;
import java.util.Objects;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedHelpers;

Expand All @@ -33,14 +36,14 @@ public CustomPrivacy(@NonNull ClassLoader classLoader, @NonNull XSharedPreferenc
}

public static JSONObject getJSON(String number) {
if (!Utils.xprefs.getBoolean("custom_privacy", true) || TextUtils.isEmpty(number))
if (Objects.equals(Utils.xprefs.getString("custom_privacy_type", "0"), "0") || TextUtils.isEmpty(number))
return new JSONObject();
return WppCore.getPrivJSON(number + "_privacy", new JSONObject());
}

@Override
public void doHook() throws Throwable {
if (!prefs.getBoolean("custom_privacy", true)) return;
if (Objects.equals(Utils.xprefs.getString("custom_privacy_type", "0"), "0")) return;

Class<?> ContactInfoActivityClass = XposedHelpers.findClass("com.whatsapp.chatinfo.ContactInfoActivity", classLoader);
Class<?> GroupInfoActivityClass = XposedHelpers.findClass("com.whatsapp.group.GroupChatInfoActivity", classLoader);
Expand All @@ -51,32 +54,54 @@ public void doHook() throws Throwable {
chatUserJidMethod = ReflectionUtils.findMethodUsingFilter(ContactInfoActivityClass, method -> method.getParameterCount() == 0 && userJidClass.isAssignableFrom(method.getReturnType()));
groupUserJidMethod = ReflectionUtils.findMethodUsingFilter(GroupInfoActivityClass, method -> method.getParameterCount() == 0 && groupJidClass.isAssignableFrom(method.getReturnType()));

var hooker = new WppCore.ActivityChangeState() {
@SuppressLint("ResourceType")
@Override
public void onChange(Object objActivity, ChangeType type) {
try {
if (type != ChangeType.START) return;
if (!ContactInfoActivityClass.isInstance(objActivity) && !GroupInfoActivityClass.isInstance(objActivity))
return;
Activity activity = (Activity) objActivity;
if (activity.findViewById(0x7f0a9999) != null) return;
int id = Utils.getID("contact_info_security_card_layout", "id");
ViewGroup infoLayout = activity.getWindow().findViewById(id);
View itemView = (View) listItemWithLeftIconClass.getConstructor(Context.class).newInstance(activity);
itemView.setId(0x7f0a9999);
listItemWithLeftIconClass.getMethod("setTitle", CharSequence.class).invoke(itemView, activity.getString(ResId.string.custom_privacy));
listItemWithLeftIconClass.getMethod("setDescription", CharSequence.class).invoke(itemView, activity.getString(ResId.string.custom_privacy_sum));
listItemWithLeftIconClass.getMethod("setIcon", int.class).invoke(itemView, ResId.drawable.ic_privacy);
itemView.setOnClickListener((v) -> showPrivacyDialog(activity, ContactInfoActivityClass.isInstance(activity)));
infoLayout.addView(itemView);
} catch (Throwable e) {
logDebug(e);
Utils.showToast(e.getMessage(), Toast.LENGTH_SHORT);
var type = Integer.parseInt(Utils.xprefs.getString("custom_privacy_type", "0"));


if (type == 1) {

var hooker = new WppCore.ActivityChangeState() {
@SuppressLint("ResourceType")
@Override
public void onChange(Object objActivity, ChangeType type) {
try {
if (type != ChangeType.START) return;
if (!ContactInfoActivityClass.isInstance(objActivity) && !GroupInfoActivityClass.isInstance(objActivity))
return;
Activity activity = (Activity) objActivity;
if (activity.findViewById(0x7f0a9999) != null) return;
int id = Utils.getID("contact_info_security_card_layout", "id");
ViewGroup infoLayout = activity.getWindow().findViewById(id);
View itemView = (View) listItemWithLeftIconClass.getConstructor(Context.class).newInstance(activity);
itemView.setId(0x7f0a9999);
listItemWithLeftIconClass.getMethod("setTitle", CharSequence.class).invoke(itemView, activity.getString(ResId.string.custom_privacy));
listItemWithLeftIconClass.getMethod("setDescription", CharSequence.class).invoke(itemView, activity.getString(ResId.string.custom_privacy_sum));
listItemWithLeftIconClass.getMethod("setIcon", int.class).invoke(itemView, ResId.drawable.ic_privacy);
itemView.setOnClickListener((v) -> showPrivacyDialog(activity, ContactInfoActivityClass.isInstance(activity)));
infoLayout.addView(itemView);
} catch (Throwable e) {
logDebug(e);
Utils.showToast(e.getMessage(), Toast.LENGTH_SHORT);
}
}
}
};
WppCore.addListenerActivity(hooker);
};
WppCore.addListenerActivity(hooker);
} else if (type == 2) {
var hooker = new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
var menu = (Menu) param.args[0];
var activity = (Activity) param.thisObject;
var customPrivacy = menu.add(0, 0, 0, ResId.string.custom_privacy);
customPrivacy.setIcon(ResId.drawable.ic_privacy);
customPrivacy.setOnMenuItemClickListener(item -> {
showPrivacyDialog(activity, ContactInfoActivityClass.isInstance(activity));
return true;
});
}
};
XposedHelpers.findAndHookMethod(ContactInfoActivityClass, "onCreateOptionsMenu", Menu.class, hooker);
XposedHelpers.findAndHookMethod(GroupInfoActivityClass, "onCreateOptionsMenu", Menu.class, hooker);
}
}

private void showPrivacyDialog(Activity activity, boolean isChat) {
Expand All @@ -91,24 +116,34 @@ private void showPrivacyDialog(Activity activity, boolean isChat) {
var number = WppCore.stripJID(rawJid);
var builder = new AlertDialogWpp(activity);
builder.setTitle(ResId.string.custom_privacy);
String[] items = {activity.getString(ResId.string.hideread), activity.getString(ResId.string.hidestatusview), activity.getString(ResId.string.hidereceipt), activity.getString(ResId.string.ghostmode), activity.getString(ResId.string.ghostmode_r)};
String[] itemsKeys = {"HideSeen", "HideViewStatus", "HideReceipt", "HideTyping", "HideRecording"};
String[] globalKeys = {"hideread", "hidestatusview", "hidereceipt", "ghostmode_t", "ghostmode_r"};
String[] items = {activity.getString(ResId.string.hideread), activity.getString(ResId.string.hidestatusview), activity.getString(ResId.string.hidereceipt), activity.getString(ResId.string.ghostmode), activity.getString(ResId.string.ghostmode_r), activity.getString(ResId.string.block_call)};
String[] itemsKeys = {"HideSeen", "HideViewStatus", "HideReceipt", "HideTyping", "HideRecording", "BlockCall"};
String[] globalKeys = {"hideread", "hidestatusview", "hidereceipt", "ghostmode_t", "ghostmode_r", "call_privacy"};


// load prefs
boolean[] checkedItems = new boolean[items.length];
var json = CustomPrivacy.getJSON(number);
for (int i = 0; i < itemsKeys.length; i++) {
checkedItems[i] = json.optBoolean(itemsKeys[i], prefs.getBoolean(globalKeys[i], false));
if (globalKeys[i].equals("call_privacy")) {
checkedItems[i] = json.optBoolean(itemsKeys[i], Objects.equals(prefs.getString(globalKeys[i], "0"), "1"));
} else {
checkedItems[i] = json.optBoolean(itemsKeys[i], prefs.getBoolean(globalKeys[i], false));
}
}

builder.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> checkedItems[which] = isChecked);
builder.setPositiveButton("OK", (dialog, which) -> {
try {
JSONObject jsonObject = new JSONObject();
for (int i = 0; i < itemsKeys.length; i++) {
if (prefs.getBoolean(globalKeys[i], false) != checkedItems[i])
jsonObject.put(itemsKeys[i], checkedItems[i]);
if (globalKeys[i].equals("call_privacy")) {
if (Objects.equals(prefs.getString(globalKeys[i], "0"), "1") != checkedItems[i])
jsonObject.put(itemsKeys[i], checkedItems[i]);
} else {
if (prefs.getBoolean(globalKeys[i], false) != checkedItems[i])
jsonObject.put(itemsKeys[i], checkedItems[i]);
}
}
WppCore.setPrivJSON(number + "_privacy", jsonObject);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
var number = WppCore.stripJID(jid);
var privacy = CustomPrivacy.getJSON(number);
var customHideRead = privacy.optBoolean("HideSeen", hideread);
var cutomHideStatusView = privacy.optBoolean("HideViewStatus", hidestatusview);

if (WppCore.isGroup(jid)) {
if (privacy.optBoolean("HideSeen", hideread_group) || ghostmode) {
param.setResult(null);
}
} else if (jid.startsWith("status")) {
if (cutomHideStatusView || ghostmode) {
var participant = (String) XposedHelpers.getObjectField(srj, "participant");
var customHideStatusView = CustomPrivacy.getJSON(WppCore.stripJID(participant)).optBoolean("HideViewStatus", hidestatusview);
if (customHideStatusView || ghostmode) {
param.setResult(null);
}
} else if (customHideRead || ghostmode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,11 @@ public static void debugMethods(Class<?> cls, Object thisObject) {
}
}
}

public static void debugObject(Object srj) {
if (srj == null) return;
XposedBridge.log("DEBUG OBJECT: " + srj.getClass().getName());
debugFields(srj.getClass(), srj);
debugMethods(srj.getClass(), srj);
}
}
10 changes: 10 additions & 0 deletions app/src/main/res/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,14 @@
<item>Flip</item>
<item>Hyperspace out</item>
</string-array>
<string-array name="custom_privacy_entries">
<item>@string/antirevoke_buttons_desable</item>
<item>@string/show_contact_info</item>
<item>@string/show_contact_info_menu</item>
</string-array>
<string-array name="custom_privacy_values">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
</resources>
8 changes: 5 additions & 3 deletions app/src/main/res/xml/fragment_privacy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
app:iconSpaceReserved="false"
app:title="@string/conversation">

<rikka.material.preference.MaterialSwitchPreference
app:defaultValue="true"
app:key="custom_privacy"
<ListPreference
app:defaultValue="0"
app:entries="@array/custom_privacy_entries"
app:entryValues="@array/custom_privacy_values"
app:key="custom_privacy_type"
app:summary="@string/custom_privacy_per_contact_sum"
app:title="@string/custom_privacy_per_contact" />

Expand Down

0 comments on commit bc425d8

Please sign in to comment.