mirror of
https://github.com/MGislv/NekoX.git
synced 2024-07-04 11:13:36 +00:00
Support add clash url as subscription
This commit is contained in:
parent
bc2261efd9
commit
06df6a63dd
|
@ -1,8 +1,8 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
def verName = "7.4.2-rc06"
|
def verName = "7.4.2-rc07"
|
||||||
def verCode = 186
|
def verCode = 188
|
||||||
|
|
||||||
def officialVer = "7.4.2"
|
def officialVer = "7.4.2"
|
||||||
def officialCode = 2227
|
def officialCode = 2227
|
||||||
|
@ -85,6 +85,7 @@ dependencies {
|
||||||
implementation 'cn.hutool:hutool-crypto:5.5.8'
|
implementation 'cn.hutool:hutool-crypto:5.5.8'
|
||||||
implementation 'cn.hutool:hutool-http:5.5.8'
|
implementation 'cn.hutool:hutool-http:5.5.8'
|
||||||
|
|
||||||
|
implementation 'org.yaml:snakeyaml:1.27'
|
||||||
implementation 'com.jakewharton:process-phoenix:2.0.0'
|
implementation 'com.jakewharton:process-phoenix:2.0.0'
|
||||||
|
|
||||||
implementation project(":openpgp-api")
|
implementation project(":openpgp-api")
|
||||||
|
@ -138,7 +139,7 @@ pwd = pwd ?: System.getenv("ALIAS_PASS")
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
buildToolsVersion '30.0.3'
|
buildToolsVersion '30.0.3'
|
||||||
ndkVersion rootProject.ext.ndkVersion
|
ndkVersion '21.3.6528147'
|
||||||
|
|
||||||
defaultConfig.applicationId = "nekox.messenger"
|
defaultConfig.applicationId = "nekox.messenger"
|
||||||
|
|
||||||
|
|
|
@ -32,16 +32,17 @@ import org.telegram.messenger.R
|
||||||
init {
|
init {
|
||||||
add(NoPlugin)
|
add(NoPlugin)
|
||||||
addAll(ApplicationLoader.applicationContext.packageManager.queryIntentContentProviders(
|
addAll(ApplicationLoader.applicationContext.packageManager.queryIntentContentProviders(
|
||||||
Intent(PluginContract.ACTION_NATIVE_PLUGIN), PackageManager.GET_META_DATA).map { NativePlugin(it) })
|
Intent(PluginContract.ACTION_NATIVE_PLUGIN), PackageManager.GET_META_DATA)
|
||||||
|
.map { NativePlugin(it) })
|
||||||
}
|
}
|
||||||
|
|
||||||
val lookup = mutableMapOf<String, Plugin>().apply {
|
val lookup = mutableMapOf<String, Plugin>().apply {
|
||||||
for (plugin in this@PluginList) {
|
for (plugin in this@PluginList) {
|
||||||
fun check(old: Plugin?) = check(old == null || old === plugin) { LocaleController.formatString("SSPluginConflictingName",R.string.SSPluginConflictingName,plugin.id) }
|
fun check(old: Plugin?) = check(old == null || old === plugin) { LocaleController.formatString("SSPluginConflictingName",R.string.SSPluginConflictingName,plugin.id) }
|
||||||
check(put(plugin.id, plugin))
|
check(put(plugin.id, plugin))
|
||||||
for (alias in plugin.idAliases) check(put(alias, plugin))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val lookupNames get() = lookup.values.map {
|
val lookupNames get() = lookup.values.map {
|
||||||
if (it.label.isNotBlank() && it.id.isNotBlank()) {
|
if (it.label.isNotBlank() && it.id.isNotBlank()) {
|
||||||
"${it.label} (${it.id})"
|
"${it.label} (${it.id})"
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||||
import cn.hutool.core.codec.Base64
|
import cn.hutool.core.codec.Base64
|
||||||
import com.github.shadowsocks.plugin.PluginConfiguration
|
import com.github.shadowsocks.plugin.PluginConfiguration
|
||||||
import com.github.shadowsocks.plugin.PluginManager
|
import com.github.shadowsocks.plugin.PluginManager
|
||||||
|
import com.github.shadowsocks.plugin.PluginOptions
|
||||||
import com.v2ray.ang.V2RayConfig.SS_PROTOCOL
|
import com.v2ray.ang.V2RayConfig.SS_PROTOCOL
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
@ -106,16 +107,29 @@ class ShadowsocksLoader {
|
||||||
|
|
||||||
val pl = PluginConfiguration(plugin)
|
val pl = PluginConfiguration(plugin)
|
||||||
|
|
||||||
if (pl.selected.contains("v2ray") && pl.selected != "v2ray") {
|
if (pl.selected.contains("v2ray") && pl.selected != "v2ray-plugin") {
|
||||||
|
|
||||||
pl.pluginsOptions["v2ray"] = pl.getOptions()
|
pl.pluginsOptions["v2ray-plugin"] = pl.getOptions().apply { id = "v2ray-plugin" }
|
||||||
pl.pluginsOptions.remove(pl.selected)
|
pl.pluginsOptions.remove(pl.selected)
|
||||||
pl.selected = "v2ray"
|
pl.selected = "v2ray-plugin"
|
||||||
|
|
||||||
// reslove v2ray plugin
|
// reslove v2ray plugin
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pl.selected == "obfs") {
|
||||||
|
|
||||||
|
pl.pluginsOptions["obfs-local"] = pl.getOptions().apply { id = "obfs-local" }
|
||||||
|
pl.pluginsOptions.remove(pl.selected)
|
||||||
|
pl.selected = "obfs-local"
|
||||||
|
|
||||||
|
// reslove clash obfs
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin = pl.toString()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
@ -150,6 +164,23 @@ class ShadowsocksLoader {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
fun parseJson(ssObj: JSONObject): Bean {
|
||||||
|
var pluginStr = ""
|
||||||
|
val pId = ssObj.optString("plugin")
|
||||||
|
if (!pId.isNullOrBlank()) {
|
||||||
|
val plugin = PluginOptions(pId, ssObj.optString("plugin_opts"))
|
||||||
|
pluginStr = plugin.toString(false)
|
||||||
|
}
|
||||||
|
return Bean(
|
||||||
|
ssObj.getString("server"),
|
||||||
|
ssObj.getInt("server_port"),
|
||||||
|
ssObj.getString("password"),
|
||||||
|
ssObj.getString("method"),
|
||||||
|
pluginStr,
|
||||||
|
ssObj.optString("remarks")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun parse(url: String): Bean {
|
fun parse(url: String): Bean {
|
||||||
|
|
||||||
if (url.contains("@")) {
|
if (url.contains("@")) {
|
||||||
|
@ -157,7 +188,7 @@ class ShadowsocksLoader {
|
||||||
// ss-android style
|
// ss-android style
|
||||||
|
|
||||||
val link = url.replace(SS_PROTOCOL, "https://").toHttpUrlOrNull()
|
val link = url.replace(SS_PROTOCOL, "https://").toHttpUrlOrNull()
|
||||||
?: error("invalid ss-android link $url")
|
?: error("invalid ss-android link $url")
|
||||||
|
|
||||||
if (link.password.isNotBlank()) {
|
if (link.password.isNotBlank()) {
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ class ShadowsocksRLoader {
|
||||||
var remarks: String? = null
|
var remarks: String? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val hash = (host + remotePort + password + protocol + obfs + method).hashCode()
|
val hash get() = (host + remotePort + password + protocol + obfs + method).hashCode()
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
return super.equals(other) || (other is Bean && hash == other.hash)
|
return super.equals(other) || (other is Bean && hash == other.hash)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@file:Suppress("UNCHECKED_CAST")
|
||||||
|
|
||||||
package tw.nekomimi.nekogram.utils
|
package tw.nekomimi.nekogram.utils
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
@ -20,20 +22,28 @@ import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.setPadding
|
import androidx.core.view.setPadding
|
||||||
|
import com.github.shadowsocks.plugin.PluginOptions
|
||||||
import com.google.zxing.*
|
import com.google.zxing.*
|
||||||
import com.google.zxing.common.GlobalHistogramBinarizer
|
import com.google.zxing.common.GlobalHistogramBinarizer
|
||||||
import com.google.zxing.qrcode.QRCodeReader
|
import com.google.zxing.qrcode.QRCodeReader
|
||||||
import com.google.zxing.qrcode.QRCodeWriter
|
import com.google.zxing.qrcode.QRCodeWriter
|
||||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
||||||
|
import com.v2ray.ang.V2RayConfig
|
||||||
import com.v2ray.ang.V2RayConfig.SSR_PROTOCOL
|
import com.v2ray.ang.V2RayConfig.SSR_PROTOCOL
|
||||||
import com.v2ray.ang.V2RayConfig.SS_PROTOCOL
|
import com.v2ray.ang.V2RayConfig.SS_PROTOCOL
|
||||||
import com.v2ray.ang.V2RayConfig.TROJAN_PROTOCOL
|
import com.v2ray.ang.V2RayConfig.TROJAN_PROTOCOL
|
||||||
import com.v2ray.ang.V2RayConfig.VMESS1_PROTOCOL
|
import com.v2ray.ang.V2RayConfig.VMESS1_PROTOCOL
|
||||||
import com.v2ray.ang.V2RayConfig.VMESS_PROTOCOL
|
import com.v2ray.ang.V2RayConfig.VMESS_PROTOCOL
|
||||||
|
import com.v2ray.ang.dto.AngConfig
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONException
|
||||||
import org.telegram.messenger.*
|
import org.telegram.messenger.*
|
||||||
import org.telegram.messenger.browser.Browser
|
import org.telegram.messenger.browser.Browser
|
||||||
|
import org.yaml.snakeyaml.Yaml
|
||||||
import tw.nekomimi.nekogram.BottomBuilder
|
import tw.nekomimi.nekogram.BottomBuilder
|
||||||
|
import tw.nekomimi.nekogram.ShadowsocksLoader
|
||||||
|
import tw.nekomimi.nekogram.ShadowsocksRLoader
|
||||||
import tw.nekomimi.nekogram.utils.AlertUtil.showToast
|
import tw.nekomimi.nekogram.utils.AlertUtil.showToast
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.NetworkInterface
|
import java.net.NetworkInterface
|
||||||
|
@ -66,17 +76,113 @@ object ProxyUtil {
|
||||||
fun parseProxies(_text: String): MutableList<String> {
|
fun parseProxies(_text: String): MutableList<String> {
|
||||||
|
|
||||||
val text = runCatching {
|
val text = runCatching {
|
||||||
|
|
||||||
String(Base64.decode(_text, Base64.NO_PADDING))
|
String(Base64.decode(_text, Base64.NO_PADDING))
|
||||||
|
|
||||||
}.recover {
|
}.recover {
|
||||||
|
|
||||||
_text
|
_text
|
||||||
|
|
||||||
}.getOrThrow()
|
}.getOrThrow()
|
||||||
|
|
||||||
val proxies = mutableListOf<String>()
|
val proxies = mutableListOf<String>()
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
try {
|
||||||
|
// sip008
|
||||||
|
val ssArray = JSONArray(text)
|
||||||
|
for (index in 0..ssArray.length()) {
|
||||||
|
proxies.add(ShadowsocksLoader.Bean.parseJson(ssArray.getJSONObject(index)).toString())
|
||||||
|
}
|
||||||
|
return proxies
|
||||||
|
} catch (ignored: JSONException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.contains("proxies:\n")) {
|
||||||
|
// clash
|
||||||
|
|
||||||
|
for (proxy in (Yaml().loadAs(text, Map::class.java)["proxies"] as List<Map<String, Any?>>)) {
|
||||||
|
val type = proxy["type"] as String
|
||||||
|
when (type) {
|
||||||
|
"ss" -> {
|
||||||
|
var pluginStr = ""
|
||||||
|
if (proxy.contains("plugin")) {
|
||||||
|
val opts = PluginOptions()
|
||||||
|
opts.id = proxy["plugin"] as String
|
||||||
|
opts.putAll(proxy["plugin-opts"] as Map<String, String?>)
|
||||||
|
pluginStr = opts.toString(false)
|
||||||
|
}
|
||||||
|
proxies.add(ShadowsocksLoader.Bean(
|
||||||
|
proxy["server"] as String,
|
||||||
|
proxy["port"] as Int,
|
||||||
|
proxy["password"] as String,
|
||||||
|
proxy["cipher"] as String,
|
||||||
|
pluginStr,
|
||||||
|
proxy["name"] as String
|
||||||
|
).toString())
|
||||||
|
}
|
||||||
|
"vmess" -> {
|
||||||
|
val opts = AngConfig.VmessBean()
|
||||||
|
for (opt in proxy) {
|
||||||
|
when (opt.key) {
|
||||||
|
"name" -> opts.remarks = opt.value as String
|
||||||
|
"server" -> opts.address = opt.value as String
|
||||||
|
"port" -> opts.port = opt.value as Int
|
||||||
|
"uuid" -> opts.id = opt.value as String
|
||||||
|
"alterId" -> opts.alterId = opt.value as Int
|
||||||
|
"cipher" -> opts.security = opt.value as String
|
||||||
|
"network" -> opts.network = opt.value as String
|
||||||
|
"tls" -> opts.streamSecurity = if (opt.value?.toString() == "true") "tls" else opts.streamSecurity
|
||||||
|
"ws-path" -> opts.path = opt.value as String
|
||||||
|
"servername" -> opts.requestHost = opt.value as String
|
||||||
|
"h2-opts" -> for (h2Opt in (opt.value as Map<String, Any>)) {
|
||||||
|
when (h2Opt.key) {
|
||||||
|
"host" -> opts.requestHost = (h2Opt.value as List<String>).first()
|
||||||
|
"path" -> opts.path = h2Opt.value as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"http-opts" -> for (httpOpt in (opt.value as Map<String, Any>)) {
|
||||||
|
when (httpOpt.key) {
|
||||||
|
"path" -> opts.path = (httpOpt.value as List<String>).first()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proxies.add(opts.toString())
|
||||||
|
}
|
||||||
|
"trojan" -> {
|
||||||
|
val opts = AngConfig.VmessBean()
|
||||||
|
opts.configType = V2RayConfig.EConfigType.Trojan
|
||||||
|
for (opt in proxy) {
|
||||||
|
when (opt.key) {
|
||||||
|
"name" -> opts.remarks = opt.value as String
|
||||||
|
"server" -> opts.address = opt.value as String
|
||||||
|
"port" -> opts.port = opt.value as Int
|
||||||
|
"password" -> opts.id = opt.value as String
|
||||||
|
"sni" -> opts.requestHost = opt.value as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proxies.add(opts.toString())
|
||||||
|
}
|
||||||
|
"ssr" -> {
|
||||||
|
val opts = ShadowsocksRLoader.Bean()
|
||||||
|
for (opt in proxy) {
|
||||||
|
when (opt.key) {
|
||||||
|
"name" -> opts.remarks = opt.value as String
|
||||||
|
"server" -> opts.host = opt.value as String
|
||||||
|
"port" -> opts.remotePort = opt.value as Int
|
||||||
|
"cipher" -> opts.method = opt.value as String
|
||||||
|
"password" -> opts.password = opt.value as String
|
||||||
|
"obfs" -> opts.obfs = opt.value as String
|
||||||
|
"protocol" -> opts.protocol = opt.value as String
|
||||||
|
"obfs-param" -> opts.obfs_param = opt.value as String
|
||||||
|
"protocol-param" -> opts.protocol_param = opt.value as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proxies.add(opts.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proxies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
text.split('\n').map { it.split(" ") }.forEach {
|
text.split('\n').map { it.split(" ") }.forEach {
|
||||||
|
|
||||||
it.forEach { line ->
|
it.forEach { line ->
|
||||||
|
|
|
@ -16,7 +16,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static String detectNdkVersion() {
|
/*static String detectNdkVersion() {
|
||||||
|
|
||||||
def version = "21.3.6528147"
|
def version = "21.3.6528147"
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ ext {
|
||||||
|
|
||||||
ndkVersion = detectNdkVersion()
|
ndkVersion = detectNdkVersion()
|
||||||
|
|
||||||
}
|
}*/
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
|
|
@ -7,7 +7,7 @@ plugins {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
ndkVersion = rootProject.extra.get("ndkVersion").toString()
|
ndkVersion = "21.3.6528147"
|
||||||
|
|
||||||
compileSdkVersion(30)
|
compileSdkVersion(30)
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
|
|
@ -4,8 +4,8 @@ plugins {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
buildToolsVersion '30.0.2'
|
buildToolsVersion '30.0.3'
|
||||||
ndkVersion rootProject.ext.ndkVersion
|
ndkVersion "21.3.6528147"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
|
|
Loading…
Reference in New Issue
Block a user