Android library module (com.v2ray.devicekit)
- DeviceKit settings UI (HWID/User-Agent overrides) provided via AndroidX Preferences resources.
- "Happ" links decryption/expansion helpers.
- Custom v2rayNG fork (this addon is integrated here):
lolka1333/v2rayNG
This repository is an Android library module intended to be added into the upstream v2rayNG project as:
- a git submodule under
V2rayNG/devicekit - a Gradle included module (
include(":devicekit"))
It does not replace the app: you still need a few small edits in v2rayNG to:
- show DeviceKit settings screen
- apply HWID / User-Agent headers to subscription requests
- support importing / storing
happ://crypt4/...subscription URLs
This repo is intended to be used as a git submodule inside the v2rayNG repo.
From the root of your v2rayNG repository:
git submodule add https://github.com/lolka1333/v2rayNG-DeviceKit-Addon.git V2rayNG/devicekit
git submodule update --init --recursiveTo update later:
git submodule update --remote --merge V2rayNG/devicekitExample layout:
v2rayNG/V2rayNG/devicekit(submodule)
In v2rayNG/V2rayNG/settings.gradle.kts:
include(":devicekit")In v2rayNG/V2rayNG/app/build.gradle.kts:
dependencies {
implementation(project(":devicekit"))
}File:
v2rayNG/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SettingsActivity.kt
In SettingsActivity.SettingsFragment.onCreatePreferences(...) after your main preferences are loaded:
- Import:
import com.v2ray.devicekit.SettingsUi- Call after
addPreferencesFromResource(...)and after your summary initialization:
addPreferencesFromResource(R.xml.pref_settings)
// Install DeviceKit preferences (adds pref_devicekit.xml + binds logic)
SettingsUi.install(this)SettingsUi.install() appends pref_devicekit.xml to the existing PreferenceScreen, installs summary providers (so values are visible without clicking) and then calls UiBinder.bind().
This library is not only UI. For full functionality you need to apply it in the places below.
If you already have some of these edits — keep them, just compare with this list.
File:
v2rayNG/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
- Add imports:
import com.v2ray.devicekit.Compat
import com.v2ray.devicekit.Kit- Decrypt Happ-style subscription URLs before opening connection:
val effectiveUrl = Compat.decryptSubscriptionUrl(currentUrl) ?: currentUrl
val conn = createProxyConnection(effectiveUrl, httpPort, timeout, timeout) ?: continue- Apply DeviceKit headers from settings to the request:
Kit.applyToConnectionFromSettings(
conn = conn,
context = com.v2ray.ang.AngApplication.application,
subscriptionUserAgent = userAgent,
defaultUserAgent = "v2rayNG/${BuildConfig.VERSION_NAME}",
appVersionName = BuildConfig.VERSION_NAME,
)This ensures:
happ://crypt4/...subscription links work in background updates (they are decrypted before request)- HWID / custom UA are applied to
HttpURLConnectionaccording to DeviceKit settings
File:
v2rayNG/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubEditActivity.kt
Goal:
- Allow user to paste
happ://crypt4/...into the UI - Validate decrypted URL
- Save decrypted URL into MMKV (so it displays as plain
https://...and works everywhere)
Required change inside saveServer() (after reading subItem.url):
val validateUrl = Compat.decryptSubscriptionUrl(subItem.url) ?: subItem.url
subItem.url = validateUrl
if (!Utils.isValidUrl(validateUrl)) {
toast(R.string.toast_invalid_url)
return false
}
if (!Utils.isValidSubUrl(validateUrl)) {
toast(R.string.toast_insecure_url_protocol)
if (!subItem.allowInsecureUrl) {
return false
}
}File:
v2rayNG/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
In parseBatchSubscription(...), when iterating lines:
val decrypted = Compat.decryptSubscriptionUrl(str)
if (Utils.isValidSubUrl(decrypted)) {
count += importUrlAsSubscription(str)
}This makes the importer treat happ://crypt4/... as a subscription URL.
In importUrlAsSubscription(url: String) keep it small:
val decryptedUrl = Compat.decryptSubscriptionUrl(url) ?: url
val subscriptions = MmkvManager.decodeSubscriptions()
subscriptions.forEach {
if (it.subscription.url == url || it.subscription.url == decryptedUrl) {
return 0
}
}
val uri = URI(Utils.fixIllegalUrl(decryptedUrl))
val subItem = SubscriptionItem()
subItem.remarks = uri.fragment ?: "import sub"
subItem.url = decryptedUrl
MmkvManager.encodeSubscription("", subItem)
return 1Result:
- you can paste
happ://crypt4/...into clipboard import - it will be saved as plain
https://... - repeated imports won’t create duplicates
When you add this repo as a submodule, the v2rayNG project will use whatever commit the submodule points to.
To update DeviceKit inside v2rayNG later:
git submodule update --remote --merge V2rayNG/devicekitDon’t forget to commit the submodule pointer in your v2rayNG repo.
- If you accept multiline subscriptions/links in text fields, use:
val expanded = Compat.expandHappLinksInText(text)This module uses libs.versions.toml aliases:
libs.mmkv.staticlibs.preference.ktx
So your v2rayNG project must have these aliases in its gradle/libs.versions.toml.
If they don’t exist, either add them to the catalog or replace devicekit dependencies with explicit coordinates.
val url = Compat.decryptSubscriptionUrl(rawUrl)val expanded = Compat.expandHappLinksInText(text)minSdk = 24- Uses AndroidX Preference (
androidx.preference:preference-ktx) and MMKV.