Skip to content

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:

./gradlew packageAllPlugins -PjsTool=bun