Valenced Native Kotlin Development
Native plugins execute pre-compiled DEX bytecode inside Discord\'s ClassLoader before Hermes JS initializes.
1. Plugin Structure
A native plugin contains a Kotlin source tree under src/main/kotlin and a manifest.json:
{
"format": 1,
"id": "rosie.my-native-plugin",
"name": "My Native Plugin",
"description": "High performance native hooks for Discord Android.",
"author": "Rosie",
"icon": "ic_cog_24px",
"version": "1.0.0",
"dependencies": {
"valenced.api": {
"version": ">=1 <2"
},
"discord": {
"version": "*"
}
},
"dist": {
"script": "index.js",
"android": {
"path": "plugin.jar",
"class": "com.rosie.myplugin.MyPlugin"
}
}
}
2. Kotlin DSL Entrypoint (MyPlugin.kt)
Annotate the file with @file:JvmName(...) matching dist.android.class:
@file:JvmName("MyPlugin")
package com.rosie.myplugin
import android.widget.TextView
import app.valenced.plugins.plugin
import app.valenced.xposed.api.registerNativeMethod
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
val myPlugin = plugin {
start {
registerNativeMethod("rosie.my-native-plugin.setSpacing") { args ->
val spacing = (args.firstOrNull() as? Number)?.toFloat() ?: 0.0f
"Spacing set to: $spacing"
}
try {
val textViewClass = XposedHelpers.findClass(
"com.discord.chat.presentation.message.view.MessageContentView",
classLoader
)
XposedBridge.hookAllMethods(
textViewClass,
"setLineSpacing",
object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam) {
param.args[0] = 0.0f
param.args[1] = 1.0f
}
}
)
} catch (t: Throwable) {
}
}
stop {
}
}
3. Compilation & Packaging
Compile all native plugins into build/outputs/plugin/plugin.jar using Gradle and D8 desugaring: