### Build and Install WaEnhancer Source: https://context7.com/dev4mod/waenhancer/llms.txt Commands to build WaEnhancer for WhatsApp or WhatsApp Business flavors, and to install and auto-launch on a connected device. ```bash # Supported WhatsApp versions: 2.26.16.xx (and variants listed in arrays.xml) # Min Android SDK: 28 (Android 9 Pie) # Supported ABIs: armeabi-v7a, arm64-v8a, x86, x86_64 # Installation steps: # 1. Root device and install LSPosed (https://github.com/JingMatrix/LSPosed) # 2. Download WaEnhancer APK from GitHub Actions CI artifacts # 3. Install APK: adb install WaEnhancer-whatsapp-debug.apk # 4. Open LSPosed Manager → Modules → Enable "WaEnhancer" # 5. Set scope to "WhatsApp" (com.whatsapp) and/or "WhatsApp Business" (com.whatsapp.w4b) # 6. Force-stop and restart WhatsApp # 7. Open WaEnhancer app to configure features # Build from source (Android Studio or CLI): ./gradlew assembleWhatsappDebug # WhatsApp flavor ./gradlew assembleBusinessDebug # WhatsApp Business flavor # Install and auto-launch on connected device: ./gradlew installWhatsappDebug # Builds, installs, and force-restarts WhatsApp via adb ``` -------------------------------- ### Implement a Custom Feature Plugin Source: https://context7.com/dev4mod/waenhancer/llms.txt Example of creating a custom feature plugin by extending the base Feature class. It requires implementing doHook() for registering Xposed hooks and getPluginName() for identification. ```java public class MyCustomFeature extends Feature { public MyCustomFeature(ClassLoader classLoader, XSharedPreferences preferences) { super(classLoader, preferences); } @Override public void doHook() throws Throwable { if (!prefs.getBoolean("my_feature_enabled", false)) return; // Find target class from WhatsApp's obfuscated code using DexKit Class targetClass = Unobfuscator.loadSomeClass(classLoader); Method targetMethod = Unobfuscator.loadSomeMethod(classLoader); XposedBridge.hookMethod(targetMethod, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { log("Intercepted call with args: " + Arrays.toString(param.args)); // Modify behavior: change args, set result, or call param.setThrowable() param.args[0] = transformValue(param.args[0]); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { logDebug("Result was: " + param.getResult()); } }); } @NonNull @Override public String getPluginName() { return "MyCustomFeature"; } } ``` -------------------------------- ### WaEnhancer Module Entry Point - WppXposed Source: https://context7.com/dev4mod/waenhancer/llms.txt The main Xposed hook class that handles package loading, resource injection, disabling secure flags, and starting the feature loader. It implements Xposed interfaces for load package, init package resources, and Zygote init. ```java // assets/xposed_init contains: // com.wmods.wppenhacer.WppXposed public class WppXposed implements IXposedHookLoadPackage, IXposedHookInitPackageResources, IXposedHookZygoteInit { @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { String packageName = lpparam.packageName; // When loading WaEnhancer itself, report Xposed is active and enable world-readable prefs if (packageName.equals(BuildConfig.APPLICATION_ID)) { XposedHelpers.findAndHookMethod( MainActivity.class.getName(), lpparam.classLoader, "isXposedEnabled", XC_MethodReplacement.returnConstant(true)); return; } // Hook package visibility scope bypass, anti-updater, and version patcher AntiUpdater.hookSession(lpparam); Patch.handleLoadPackage(lpparam, getPref()); ScopeHook.hook(lpparam); // Load all features into WhatsApp or WhatsApp Business if (packageName.equals(FeatureLoader.PACKAGE_WPP) || packageName.equals(FeatureLoader.PACKAGE_BUSINESS)) { FeatureLoader.start(lpparam.classLoader, getPref(), lpparam.appInfo.sourceDir); disableSecureFlag(); // Allow screenshots by stripping FLAG_SECURE } } // Injects module string/array/drawable resources into WhatsApp's resource table @Override public void handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam) { XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res); for (var field : ResId.string.class.getFields()) { var r = R.string.class.getField(field.getName()); field.set(null, resparam.res.addResource(modRes, r.getInt(null))); } // Repeat for ResId.array and ResId.drawable fields } // Strips FLAG_SECURE from all Window.setFlags / addFlags calls public void disableSecureFlag() { XposedHelpers.findAndHookMethod(Window.class, "setFlags", int.class, int.class, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) { param.args[0] = (int) param.args[0] & ~WindowManager.LayoutParams.FLAG_SECURE; param.args[1] = (int) param.args[1] & ~WindowManager.LayoutParams.FLAG_SECURE; } }); } } ``` -------------------------------- ### WhatsApp UI CSS Theming Example Source: https://context7.com/dev4mod/waenhancer/llms.txt This CSS defines global configuration and styles for various WhatsApp UI elements using resource IDs, class+ID selectors, and pseudo-selectors. It supports properties like background-color, color-tint, border-radius, font-size, display, and transforms. ```css /* Global configuration block — must be at the start of the CSS file */ /* author = "My Theme" change_dpi = 390 change_colors = true primary_color = #1F1F1F secondary_color = #252525 background_color = #121212 bubble_colors = true bubble_right = #1e3a5f bubble_left = #2a2a2a wallpaper = true wallpaper_file = "bg.jpg" wallpaper_alpha = 55 */ /* Target a view by its Android resource ID */ #fab { background-color: #128C7E #0066CC; /* Replace WhatsApp green with blue */ color-tint: #FFFFFF; border-radius: 28px; box-shadow: 4px 4px 12px #000000; } /* Scope a selector to a specific Activity class */ .com.whatsapp.HomeActivity #conversations_row_contact_name { color: #E8E8E8; font-size: 16px; font-weight: bold; } /* Pseudo-selector: hide the nth child of a parent */ .com.whatsapp.HomeActivity LinearLayout:nth-child(3) { display: none; } /* Pseudo-selector: hide a TextView containing specific text */ TextView:contains(Archived) { display: none; } /* Gradient background */ #content { background: linear-gradient(180deg, #0d0d0d 0%, #1a1a2e 100%); } /* Move and reposition a view within its parent */ #conversation_row_date { parent: #conversation_contact_name_holder; bottom: 0px; right: 16px; font-size: 11px; color: #888888; } /* Image tint with state list (normal / pressed / disabled) */ #bottom_bar { color-tint: #AAAAAA #FFFFFF #555555; } /* Opacity and transform */ #status_button { opacity: 0.7; transform: scale(0.9); } /* Material You dynamic color token */ #toolbar { background: color_system_accent1_700; } ``` -------------------------------- ### Get My Name and Bio with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt Retrieve the logged-in user's display name using WppCore.getMyName, which reads from "startup_prefs". WppCore.getMyBio retrieves the user's bio from WhatsApp's preferences. ```java // Get the logged-in user's display name and bio String myName = WppCore.getMyName(); // From "startup_prefs" → "push_name" String myBio = WppCore.getMyBio(); // From WhatsApp's preferences → "my_current_status" ``` -------------------------------- ### Check Activity States with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt WppCore provides methods to check if the user is currently in a chat (isConversationResumed), on the home screen (isHomeActivityResumed), or to get the current activity instance. ```java // Check activity states boolean inChat = WppCore.isConversationResumed(); boolean atHome = WppCore.isHomeActivityResumed(); Activity current = WppCore.getCurrentActivity(); ``` -------------------------------- ### Get Textarea Content JavaScript Source: https://github.com/dev4mod/waenhancer/blob/master/app/src/main/assets/css_editor.html Retrieves the current value from the HTML element with the ID 'code'. This is typically used to get user-entered CSS. ```javascript function getTextareaContent() { return document.getElementById('code').value; } ``` -------------------------------- ### Resolve Contact Names and Get Current Conversation JID with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt WppCore.getContactName resolves a contact's display name from WhatsApp's database using their JID. WppCore.getCurrentUserJid retrieves the JID of the currently open conversation. ```java // Resolve a contact display name from WhatsApp's wa.db FMessageWpp.UserJid userJid = new FMessageWpp.UserJid("5511999999999@s.whatsapp.net"); String name = WppCore.getContactName(userJid); // Returns "John Doe" or "Whatsapp Contact" // Get the JID of the current open conversation FMessageWpp.UserJid currentJid = WppCore.getCurrentUserJid(); if (currentJid != null && currentJid.isGroup()) { Log.d("WAE", "In group: " + currentJid.getPhoneRawString()); // e.g. "123456789@g.us" } ``` -------------------------------- ### Initialize Feature Plugins with Version Check Source: https://context7.com/dev4mod/waenhancer/llms.txt This method initializes DexKit, hooks WhatsApp's application creation, checks version compatibility, and loads all feature plugins concurrently. It's called from WppXposed.handleLoadPackage. ```java public static void start(ClassLoader loader, XSharedPreferences pref, String sourceDir) { if (!Unobfuscator.initWithPath(sourceDir)) return; // Init DexKit with WhatsApp APK path Feature.DEBUG = pref.getBoolean("enablelogs", true); XposedHelpers.findAndHookMethod(Instrumentation.class, "callApplicationOnCreate", Application.class, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { mApp = (Application) param.args[0]; // Check if WhatsApp version is in the supported list PackageInfo packageInfo = mApp.getPackageManager().getPackageInfo(mApp.getPackageName(), 0); List supported = Arrays.asList( mApp.getResources().getStringArray(ResId.array.supported_versions_wpp)); boolean isSupported = supported.stream() .anyMatch(v -> packageInfo.versionName.startsWith(v.replace(".xx", ""))); if (!isSupported) { disableExpirationVersion(mApp.getClassLoader()); // Bypass version expiry if (!pref.getBoolean("bypass_version_check", false)) throw new Exception("Unsupported version: " + packageInfo.versionName); } initComponents(loader, pref); // Init FMessageWpp, WppCore, dialogs, etc. plugins(loader, pref, packageInfo.versionName); // Load all feature plugins sendEnabledBroadcast(mApp); // Notify WaEnhancer app that hook is active } }); } ``` -------------------------------- ### Initialize DexKit and Load WhatsApp Classes Source: https://context7.com/dev4mod/waenhancer/llms.txt Initializes the DexKit deobfuscation engine with the WhatsApp APK path and demonstrates loading specific classes and methods by signature or name. ```java boolean ok = Unobfuscator.initWithPath("/data/app/com.whatsapp-.../base.apk"); ``` ```java Class fMessageClass = Unobfuscator.loadFMessageClass(classLoader); ``` ```java Method sendReadReceiptMethod = Unobfuscator.loadHideViewSendReadJob(classLoader); ``` ```java Class userJidClass = Unobfuscator.findFirstClassUsingName( classLoader, StringMatchType.EndsWith, "jid.UserJid"); ``` ```java public static boolean initWithPath(String path) { try { dexkit = DexKitBridge.create(path); // Opens WhatsApp APK for DEX analysis return true; } catch (Exception e) { return false; } } ``` -------------------------------- ### Manage Private Module Preferences with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt Use WppCore.setPrivBoolean and WppCore.getPrivBoolean for cross-process safe boolean preference storage. Similarly, WppCore.setPrivString and WppCore.getPrivString manage string preferences. ```java // Read/write private module preferences (cross-process safe, using commit()) WppCore.setPrivBoolean("need_restart", true); boolean needRestart = WppCore.getPrivBoolean("need_restart", false); WppCore.setPrivString("last_jid", "5511999999999@s.whatsapp.net"); String lastJid = WppCore.getPrivString("last_jid", ""); ``` -------------------------------- ### WppCore - Core Utilities Source: https://context7.com/dev4mod/waenhancer/llms.txt The WppCore static utility class provides access to various WhatsApp internals, including sending messages, resolving contact names, creating JIDs, accessing the database, and interacting with the cross-process bridge. ```APIDOC ## WppCore Central static utility class providing access to WhatsApp internals from within feature hooks. ### Methods - **sendMessage(String phoneNumber, String message)**: Sends a WhatsApp text message to a phone number. - **sendReaction(String emoji, Object fMessage)**: Sends a reaction emoji to a message object. - **getContactName(Object userJid)**: Resolves a contact display name from WhatsApp's wa.db. - **getCurrentUserJid()**: Gets the JID of the current open conversation. - **createUserJid(String jidString)**: Creates a raw WhatsApp UserJid object. - **getPhoneJidFromUserJid(Object lidObject)**: Converts LID (linked device ID) to phone JID. - **getClientBridge()**: Accesses the AIDL bridge for file operations. - **isConversationResumed()**: Checks if the current activity is a conversation. - **isHomeActivityResumed()**: Checks if the home activity is resumed. - **getCurrentActivity()**: Gets the current activity instance. - **setPrivBoolean(String key, boolean value)**: Sets a private boolean preference. - **getPrivBoolean(String key, boolean defaultValue)**: Gets a private boolean preference. - **setPrivString(String key, String value)**: Sets a private string preference. - **getPrivString(String key, String defaultValue)**: Gets a private string preference. - **getMyName()**: Gets the logged-in user's display name. - **getMyBio()**: Gets the logged-in user's bio. - **addListenerActivity(Object listener)**: Subscribes to activity lifecycle events. ``` -------------------------------- ### Create User JIDs and Convert LIDs with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt WppCore.createUserJid creates a raw WhatsApp UserJid object. WppCore.getPhoneJidFromUserJid converts a LID (linked device ID) to a phone JID. ```java // Create a raw WhatsApp UserJid object (for use with WhatsApp's internal APIs) Object jidObj = WppCore.createUserJid("5511999999999@s.whatsapp.net"); // Convert LID (linked device ID) to phone JID Object phoneJid = WppCore.getPhoneJidFromUserJid(lidObject); ``` -------------------------------- ### Change FAB Background and Tint Color Source: https://github.com/dev4mod/waenhancer/blob/master/Theme.md Use `background-color` to replace existing colors and `color-tint` to set the tint for the Floating Action Button. ```css #fab { background-color: #00AA00 #FF5500; /* Replaces green with orange */ color-tint: #FFFFFF; } ``` -------------------------------- ### Wrap Raw FMessage Objects with FMessageWpp Source: https://context7.com/dev4mod/waenhancer/llms.txt Initialize FMessageWpp with a raw WhatsApp FMessage object to gain typed access to message properties. This wrapper requires initialization via FMessageWpp.initialize(classLoader), which is typically handled by FeatureLoader. ```java // Wrapping a raw FMessage object received in an Xposed hook XposedBridge.hookMethod(someMessageMethod, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) { Object rawFMessage = param.args[0]; // WhatsApp's internal FMessage object FMessageWpp msg = new FMessageWpp(rawFMessage); // Message key information FMessageWpp.Key key = msg.getKey(); String messageId = key.messageID; // e.g. "3EB0C1234567ABCD" boolean isFromMe = key.isFromMe; // false = received message FMessageWpp.UserJid remoteJid = key.remoteJid; // Sender JID details String phoneNumber = remoteJid.getPhoneNumber(); // "5511999999999" String rawJid = remoteJid.getPhoneRawString(); // "5511999999999@s.whatsapp.net" boolean isGroup = remoteJid.isGroup(); // true if @g.us boolean isContact = remoteJid.isContact(); // true if @s.whatsapp.net or @lid boolean isStatus = remoteJid.isStatus(); // true if status@broadcast // Text content String text = msg.getMessageStr(); // null for media-only messages // Media handling if (msg.isMediaFile()) { File mediaFile = msg.getMediaFile(); // Local cached file path int mediaType = msg.getMediaType(); // mediaType: 2=voice note, 42=view-once image, 43=view-once video, 82=view-once audio } // View-once detection (media types 42, 43, 82) if (msg.isViewOnce()) { XposedBridge.log("View once message from " + phoneNumber); } // Broadcast flag boolean isBroadcast = msg.isBroadcast(); // Database row ID (for MessageStore lookups) long rowId = msg.getRowId(); } }); ``` -------------------------------- ### Unobfuscator Cache Initialization Source: https://context7.com/dev4mod/waenhancer/llms.txt Initializes the UnobfuscatorCache for disk caching of deobfuscation results to speed up subsequent loads. ```java UnobfuscatorCache.init(mApp); ``` ```java SharedPreferences cache = UnobfuscatorCache.getInstance().sPrefsCacheHooks; ``` -------------------------------- ### Basic CSS Styling Source: https://github.com/dev4mod/waenhancer/blob/master/app/src/main/assets/css_editor.html Applies basic reset and sets the background color for the HTML and body elements. Ensures full height and width. ```css * { margin: 0; padding: 0; box-sizing: border-box; } html, body{ height: 100% !important; width: 100%; background: #2d2d2d; } ``` -------------------------------- ### Reading Feature Preferences Source: https://context7.com/dev4mod/waenhancer/llms.txt Demonstrates how to read boolean and string preferences from XSharedPreferences within a Feature subclass. Defaults are provided for each preference key. ```java // Reading feature preferences inside any Feature subclass (prefs = XSharedPreferences) boolean antiRevoke = prefs.getBoolean("antirevoke", false); boolean hideBlue = prefs.getBoolean("hide_seen", false); boolean ghostMode = prefs.getBoolean("ghost_mode", false); boolean disableViewOnce = prefs.getBoolean("disable_view_once", false); boolean freezeLastSeen = prefs.getBoolean("freeze_last_seen", false); boolean hideTyping = prefs.getBoolean("hide_typing", false); boolean hideRecording = prefs.getBoolean("hide_recording", false); boolean statusDownload = prefs.getBoolean("status_download", false); boolean downloadViewOnce = prefs.getBoolean("download_view_once", false); boolean hdImages = prefs.getBoolean("hd_image", false); boolean hdVideos = prefs.getBoolean("hd_video", false); boolean callRecording = prefs.getBoolean("call_recording", false); boolean callPrivacy = prefs.getBoolean("call_privacy", false); boolean bubbleColors = prefs.getBoolean("bubble_colors", false); boolean separateGroups = prefs.getBoolean("separate_groups", false); boolean showOnline = prefs.getBoolean("show_online", false); boolean alwaysOnline = prefs.getBoolean("always_online", false); boolean liteMode = prefs.getBoolean("lite_mode", false); boolean bootloaderSpoof = prefs.getBoolean("bootloader_spoofer", false); boolean bypassVersion = prefs.getBoolean("bypass_version_check", false); boolean updateCheck = prefs.getBoolean("update_check", true); boolean enableLogs = prefs.getBoolean("enablelogs", true); // String preferences String customCss = prefs.getString("custom_css", ""); String primaryColor = prefs.getString("primary_color", ""); String bubbleRight = prefs.getString("bubble_right_color", ""); String bubbleLeft = prefs.getString("bubble_left_color", ""); String customDownloadDir = prefs.getString("custom_download_dir", ""); String reactionEmoji = prefs.getString("custom_reaction_emoji", "❤️"); ``` -------------------------------- ### Global Configuration for WhatsApp Enhancer Source: https://github.com/dev4mod/waenhancer/blob/master/Theme.md Define global settings in a comment block at the beginning of your CSS file. These properties control aspects like author, wallpaper, and color schemes. ```css /* author = "Your Name" wallpaper = false / true wallpaper_file = "wall.png" wallpaper_alpha = 60 wallpaper_alpha_navigation = 75 wallpaper_alpha_toolbar = 60 change_colors = true primary_color = #FF0000 secondary_color = #00FF00 background_color = #151515 bubble_colors = true bubble_right = #a00000 bubble_left = #0000a0 change_dpi = 420 */ ``` -------------------------------- ### FMessageWpp - Message Wrapper Source: https://context7.com/dev4mod/waenhancer/llms.txt A wrapper around WhatsApp's obfuscated FMessage class, providing typed access to message key, sender JID, text content, media file, media type, and view-once status. ```APIDOC ## FMessageWpp A wrapper around WhatsApp's obfuscated `FMessage` class. ### Initialization - **initialize(ClassLoader classLoader)**: Must be called once to initialize the wrapper (done automatically by `FeatureLoader`). ### Methods - **getKey()**: Returns the message key information. - **getMessageStr()**: Returns the text content of the message (null for media-only messages). - **isMediaFile()**: Returns true if the message contains media. - **getMediaFile()**: Returns the local cached file path of the media. - **getMediaType()**: Returns the type of the media. - **isViewOnce()**: Returns true if the message is a view-once media. - **isBroadcast()**: Returns true if the message is from a broadcast list. - **getRowId()**: Returns the database row ID of the message. ### Nested Classes/Objects - **Key**: Contains message key information (`messageID`, `isFromMe`, `remoteJid`). - **UserJid**: Represents a user's JID with methods to get phone number, raw JID, and check if it's a group, contact, or status. ``` -------------------------------- ### Load Feature Plugins Concurrently Source: https://context7.com/dev4mod/waenhancer/llms.txt Loads all feature plugins asynchronously using a work-stealing thread pool. Each plugin is instantiated and its doHook() method is called. Errors during loading are logged and collected. ```java private static void plugins(ClassLoader loader, XSharedPreferences pref, String version) throws Exception { var classes = new Class[]{ AntiRevoke.class, HideSeen.class, ViewOnce.class, StatusDownload.class, CustomView.class, BubbleColors.class, CallRecording.class, CustomPrivacy.class, FreezeLastSeen.class, TypingPrivacy.class, /* ...~60 total */ }; var executor = Executors.newWorkStealingPool(Math.min(Runtime.getRuntime().availableProcessors(), 4)); for (var clazz : classes) { CompletableFuture.runAsync(() -> { try { var constructor = clazz.getConstructor(ClassLoader.class, XSharedPreferences.class); Feature plugin = (Feature) constructor.newInstance(loader, pref); plugin.doHook(); // Registers all Xposed hooks for this feature } catch (Throwable e) { XposedBridge.log(e); list.add(buildErrorItem(clazz.getSimpleName(), version, e)); } }, executor); } executor.shutdown(); executor.awaitTermination(15, TimeUnit.SECONDS); } ``` -------------------------------- ### Subscribe to Activity Lifecycle Events with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt Use WppCore.addListenerActivity to subscribe to activity lifecycle events. The listener receives the activity and its state change type, allowing logging or other actions when an activity resumes. ```java // Subscribe to activity lifecycle events WppCore.addListenerActivity((activity, state) -> { if (state == WppCore.ActivityChangeState.ChangeType.RESUMED) { XposedBridge.log("Activity resumed: " + activity.getClass().getSimpleName()); } }); ``` -------------------------------- ### Applying CSS Rules to Views Source: https://context7.com/dev4mod/waenhancer/llms.txt The `applyMatchingRules` method iterates through matched CSS rules and applies properties to a given view. It handles various CSS properties like visibility, color, background, font-size, and transforms. ```java // applyMatchingRules — applies CSS properties to matched views private void applyMatchingRules(View view, Map> rules) { String viewId = view.getResources().getResourceEntryName(view.getId()); String activity = WppCore.getCurrentActivity().getClass().getName(); // Check ID selector (#viewId) List matched = rules.get(viewId); // Check class+ID selector (.ActivityName #viewId) List scoped = rules.get(activity + " #" + viewId); for (CSSRule rule : merge(matched, scoped)) { for (CSSProperty prop : rule.getProperties()) { switch (prop.getName()) { case "display": view.setVisibility("none".equals(prop.getValue()) ? View.GONE : View.VISIBLE); break; case "color": if (view instanceof TextView) ((TextView) view).setTextColor(parseColor(prop.getValue())); break; case "background": view.setBackground(parseDrawable(prop.getValue())); // Supports gradients, URLs break; case "background-color": // Replace specific colors in the existing background/tint String[] colors = prop.getValue().split(" "); ColorReplacement.replace(view, parseColor(colors[0]), parseColor(colors[1])); break; case "font-size": if (view instanceof TextView) ((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_DIP, parsePx(prop.getValue())); break; case "transform": applyTransform(view, prop.getValue()); // rotate(), scale(), translateX/Y() break; // ... 25+ more properties } } } } ``` -------------------------------- ### Access Cross-Process Bridge and File Operations with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt Obtain the AIDL bridge using WppCore.getClientBridge to perform file operations in the WaEnhancer process. This method throws an exception if the bridge is not connected. ```java // Access the AIDL bridge to perform file operations in WaEnhancer's process WaeIIFace bridge = WppCore.getClientBridge(); // Throws if bridge not connected ParcelFileDescriptor fd = bridge.openFile("/sdcard/WaEnhancer/theme.css", false); ``` -------------------------------- ### Set Main Content Background Gradient Source: https://github.com/dev4mod/waenhancer/blob/master/Theme.md Apply a linear gradient to the main content background using the `background` property with angle and color stops. ```css #content { background: linear-gradient(0deg, #101010 0%, #202020 100%); } ``` -------------------------------- ### Broadcast API for Inter-Process Communication Source: https://context7.com/dev4mod/waenhancer/llms.txt Facilitates communication between the WaEnhancer module and the WaEnhancer app using Android broadcasts. Used for triggering WhatsApp restarts and checking module status. ```java // WaEnhancer app → WhatsApp: trigger a restart Intent restartIntent = new Intent("com.wmods.wppenhacer.WHATSAPP.RESTART"); restartIntent.putExtra("PKG", "com.whatsapp"); // or "com.whatsapp.w4b" restartIntent.setPackage("com.whatsapp"); context.sendBroadcast(restartIntent); // WaEnhancer app → WhatsApp: check if module is active Intent checkIntent = new Intent("com.wmods.wppenhacer.CHECK_WPP"); checkIntent.setPackage("com.whatsapp"); context.sendBroadcast(checkIntent); // WhatsApp (with module) responds with "com.wmods.wppenhacer.RECEIVER_WPP" // WhatsApp module → WaEnhancer app: module is active (sent on startup) // Received by WAFReceiver in WaEnhancer app: // action: "com.wmods.wppenhacer.RECEIVER_WPP" // extras: VERSION = WhatsApp version string (e.g. "2.24.10.77") // PKG = package name ("com.whatsapp" or "com.whatsapp.w4b") // WaEnhancer app: schedule a soft restart via preference flag Intent manualRestart = new Intent("com.wmods.wppenhacer.MANUAL_RESTART"); manualRestart.setPackage("com.whatsapp"); context.sendBroadcast(manualRestart); // Sets WppCore private pref "need_restart"=true; next HomeActivity resume // shows a "Restart WhatsApp?" dialog to the user ``` -------------------------------- ### Disable WhatsApp View-Once Restriction - ViewOnce Source: https://context7.com/dev4mod/waenhancer/llms.txt Hooks the method that checks the view-once flag for media messages. Converts 'view once' media to regular media, allowing multiple views and saving. Only modifies received messages. ```java // ViewOnce.doHook() — hooks the method that checks view-once flag Method viewOnceMethod = Unobfuscator.loadViewOnceMethod(classLoader); XposedBridge.hookMethod(viewOnceMethod, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { if (!prefs.getBoolean("disable_view_once", false)) return; int returnValue = (int) param.args[0]; // 1 = view once, 0 = normal FMessageWpp fMsg = new FMessageWpp(param.thisObject); // Only modify received messages (not sent ones) if (returnValue == 1 && !fMsg.getKey().isFromMe) { param.args[0] = 0; // Downgrade from view-once to normal media } } }); ``` -------------------------------- ### WhatsApp Status Saver - StatusDownload Source: https://context7.com/dev4mod/waenhancer/llms.txt Adds 'Download' and 'Share as Status' options to the WhatsApp status viewer. Downloads status media to categorized folders and handles file operations via a bridge for cross-process access. ```java // StatusDownload hooks into the status viewer menu system // Menu item IDs static final int MENU_ID_DOWNLOAD = 1001; static final int MENU_ID_SHARE_STATUS = 1002; // Adding menu items to status viewer (via ConversationItemListener) public MenuItem addMenu(Menu menu, FMessageWpp fMessage) { if (fMessage.getKey().isFromMe) return null; // No download for own statuses if (!fMessage.isMediaFile()) return null; // Only media statuses menu.add(0, MENU_ID_DOWNLOAD, 0, "Download"); menu.add(0, MENU_ID_SHARE_STATUS, 0, "Share as Status"); return menu.findItem(MENU_ID_DOWNLOAD); } // Handling the menu click public boolean onMenuItemClick(MenuItem item, FMessageWpp fMessage) { if (item.getItemId() == MENU_ID_DOWNLOAD) { File mediaFile = fMessage.getMediaFile(); String mimeType = MimeTypeUtils.getMimeTypeFromExtension(mediaFile.getName()); // Determine destination folder by MIME type String folder; if (mimeType.contains("video")) folder = "Status Videos"; else if (mimeType.contains("image")) folder = "Status Images"; else if (mimeType.contains("audio")) folder = "Status Sounds"; else folder = "Status Media"; // Copy file via bridge (cross-process file access) WaeIIFace bridge = WppCore.getClientBridge(); File destDir = new File(Environment.getExternalStorageDirectory(), "WaEnhancer/" + folder); bridge.createDir(destDir.getAbsolutePath()); // ... copy mediaFile to destDir using bridge.openFile() Utils.showToast("Saved to " + folder, Toast.LENGTH_SHORT); return true; } return false; } ``` -------------------------------- ### Hooking `View.onAttachedToWindow` to Apply CSS Rules Source: https://context7.com/dev4mod/waenhancer/llms.txt This Java code hooks the `onAttachedToWindow` method of the `View` class to apply custom CSS rules. It loads compiled rules from a cache and applies them to the view based on matching selectors. ```java // CustomView.doHook() — hooks View.onAttachedToWindow to apply CSS rules Method onAttachedToWindow = View.class.getMethod("onAttachedToWindow"); XposedBridge.hookMethod(onAttachedToWindow, new XC_MethodHook() { protected void afterHookedMethod(MethodHookParam param) { View view = (View) param.thisObject; String cssContent = prefs.getString("custom_css", ""); if (cssContent.isEmpty()) return; // Load compiled rules from disk cache (keyed by hash of CSS + WA version) Map> rules = CSSRuleCache.load(cssContent, waVersion); // Match view against compiled selectors and apply matching properties applyMatchingRules(view, rules); } }); ``` -------------------------------- ### Send WhatsApp Messages and Reactions with WppCore Source: https://context7.com/dev4mod/waenhancer/llms.txt Use WppCore.sendMessage to send text messages to a phone number. WppCore.sendReaction can be used to send emoji reactions to existing message objects. ```java // Send a WhatsApp text message to a phone number WppCore.sendMessage("5511999999999", "Hello from WaEnhancer!"); // Toast: "Message sent to 5511999999999" // Send a reaction emoji to a message object WppCore.sendReaction("👍", fMessageObject); // fMessageObject = WhatsApp's FMessage instance ``` -------------------------------- ### Position Conversation Date Element Source: https://github.com/dev4mod/waenhancer/blob/master/Theme.md Control the positioning of the conversation date element using `parent`, `bottom`, and `right` properties, and set its `font-size`. ```css #conversation_row_date { parent: #conversation_contact_name_holder; bottom: 0px; right: 16px; font-size: 12px; } ``` -------------------------------- ### Hide Archived Chat Hint TextView Source: https://github.com/dev4mod/waenhancer/blob/master/Theme.md Use a `TextView:contains()` selector with `display: none;` to hide specific text elements like the archived chat hint. ```css TextView:contains(Archived) { display: none; } ``` -------------------------------- ### Customize Conversation Row Name Text Source: https://github.com/dev4mod/waenhancer/blob/master/Theme.md Adjust the text color and size for conversation row names using `color` and `font-size` properties. ```css .com.whatsapp.HomeActivity #conversations_row_contact_name { color: #E0E0E0; font-size: 18px; } ``` -------------------------------- ### Bypass WhatsApp Version Expiry Source: https://context7.com/dev4mod/waenhancer/llms.txt Intercepts WhatsApp's version expiry check and sets the expiry date to December 31, 2099. This prevents forced updates and expiry dialogs. Requires XposedBridge and Unobfuscator utility. ```java // Extends WhatsApp's version expiry to year 2099 public static void disableExpirationVersion(ClassLoader classLoader) throws Exception { Class expirationClass = Unobfuscator.loadExpirationClass(classLoader); // Find the method with return type java.util.Date (the expiry date getter) Method expiryDateMethod = ReflectionUtils.findMethodUsingFilter( expirationClass, m -> m.getReturnType().equals(Date.class)); XposedBridge.hookMethod(expiryDateMethod, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Calendar cal = Calendar.getInstance(); cal.set(2099, 12, 31); param.setResult(cal.getTime()); // Always return Dec 31, 2099 } }); } // Called automatically when an unsupported WhatsApp version is detected ``` -------------------------------- ### Hooking SendReadReceiptJob to Hide Receipts Source: https://context7.com/dev4mod/waenhancer/llms.txt Hooks the SendReadReceiptJob to prevent read receipts, voice note played receipts, and view-once seen receipts from being sent. Supports global Ghost Mode and per-contact custom privacy. ```java // HideSeen hooks two places: // 1. SendReadReceiptJob — prevents the receipt network job from executing // 2. An in-chat receipt method — suppresses receipt when actively reading // Conceptual hook structure (from HideSeen.doHook): Method sendReadReceiptJobMethod = Unobfuscator.loadHideViewSendReadJob(classLoader); XposedBridge.hookMethod(sendReadReceiptJobMethod, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { FMessageWpp fMsg = new FMessageWpp(param.args[0]); FMessageWpp.Key key = fMsg.getKey(); boolean ghostMode = prefs.getBoolean("ghost_mode", false); boolean hideBlue = prefs.getBoolean("hide_seen", false); boolean hideVoice = prefs.getBoolean("hide_voice_seen", false); boolean hideViewOnce = prefs.getBoolean("hide_viewonce_seen", false); boolean shouldHide = ghostMode || hideBlue || (hideVoice && fMsg.getMediaType() == 2) || (hideViewOnce && fMsg.isViewOnce()); // Check per-contact custom privacy override String contactJid = key.remoteJid.getPhoneRawString(); if (CustomPrivacy.isHideSeenForContact(contactJid)) shouldHide = true; if (shouldHide && !key.isFromMe) { // Record in MessageHistory so HideSeenView can show the "seen but hidden" indicator MessageHistory.getInstance().addMessage(key.messageID, contactJid); param.setResult(null); // Cancel the SendReadReceiptJob — no receipt sent } } }); ``` -------------------------------- ### Intercept WhatsApp Message Deletion - AntiRevoke Source: https://context7.com/dev4mod/waenhancer/llms.txt Hooks into WhatsApp's message revocation method to prevent senders from deleting messages. Stores deletion metadata and optionally shows a toast notification. Works for both individual and group chats. ```java // AntiRevoke.doHook() — intercepts WhatsApp's message revocation method Method antiRevokeMethod = Unobfuscator.loadAntiRevokeMethod(classLoader); XposedBridge.hookMethod(antiRevokeMethod, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { if (!prefs.getBoolean("antirevoke", false)) return; FMessageWpp fMsg = new FMessageWpp(param.args[0]); FMessageWpp.Key key = fMsg.getKey(); // Only block deletion of messages from others (not our own) if (key.isFromMe) return; // Show a toast: "[Contact Name] deleted a message" if (prefs.getBoolean("toast_revoke", false)) { String name = WppCore.getContactName(key.remoteJid); Utils.showToast(name + " deleted a message", Toast.LENGTH_SHORT); } // Store the deletion timestamp in the database DelMessageStore.getInstance().addDeletedMessage( key.remoteJid.getPhoneRawString(), key.messageID, System.currentTimeMillis() ); // Prevent deletion by returning true (message is kept locally) param.setResult(true); } }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.