diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt index a196269f..eff2c8c2 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -11,6 +11,7 @@ object AppConfig { const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid" const val PREF_CURR_CONFIG_NAME = "pref_v2ray_config_name" const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain" + const val PREF_CURR_CONFIG_OUTBOUND_TAGS = "pref_v2ray_config_outbound_tags" const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium" const val VMESS_PROTOCOL: String = "vmess://" const val SS_PROTOCOL: String = "ss://" @@ -50,4 +51,4 @@ object AppConfig { const val MSG_STATE_STOP = 4 const val MSG_STATE_STOP_SUCCESS = 41 const val MSG_STATE_RESTART = 5 -} \ No newline at end of file +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt index 84941c07..548ffbeb 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt @@ -11,11 +11,14 @@ import android.content.IntentFilter import android.content.pm.PackageManager import android.graphics.Color import android.net.* -import android.net.VpnService -import android.os.* +import android.os.Build +import android.os.ParcelFileDescriptor +import android.os.StrictMode import android.support.annotation.RequiresApi import android.support.v4.app.NotificationCompat +import android.util.Log import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.TAG_DIRECT import com.v2ray.ang.R import com.v2ray.ang.extension.defaultDPreference import com.v2ray.ang.extension.toSpeedString @@ -24,16 +27,14 @@ import com.v2ray.ang.ui.PerAppProxyActivity import com.v2ray.ang.ui.SettingsActivity import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.Utils +import go.Seq import libv2ray.Libv2ray import libv2ray.V2RayVPNServiceSupportsSet +import org.jetbrains.anko.doAsync import rx.Observable import rx.Subscription import java.io.File import java.lang.ref.SoftReference -import android.os.Build -import android.util.Log -import go.Seq -import org.jetbrains.anko.doAsync class V2RayVpnService : VpnService() { companion object { @@ -400,24 +401,34 @@ class V2RayVpnService : VpnService() { if (mSubscription == null && v2rayPoint.isRunning && defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) { - val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "") var last_zero_speed = false + val outboundTags = defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet()) + outboundTags.remove(TAG_DIRECT) mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS) .subscribe { - val proxyUplink = v2rayPoint.queryStats("proxy", "uplink") - val proxyDownlink = v2rayPoint.queryStats("proxy", "downlink") - val directUplink = v2rayPoint.queryStats("direct", "uplink") - val directDownlink = v2rayPoint.queryStats("direct", "downlink") - val zero_speed = (proxyUplink == 0L && proxyDownlink == 0L && directUplink == 0L && directDownlink == 0L) val queryTime = System.currentTimeMillis() + val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 + var proxyTotal = 0L + val text = StringBuilder() + outboundTags.forEach { + val up = v2rayPoint.queryStats(it, "uplink") + val down = v2rayPoint.queryStats(it, "downlink") + if (up + down > 0) { + appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds) + proxyTotal += up + down + } + } + val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink") + val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink") + val zero_speed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L) if (!zero_speed || !last_zero_speed) { - val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 - updateNotification("proxy\t• ${(proxyUplink / sinceLastQueryInSeconds).toLong().toSpeedString()}↑ " + - "${(proxyDownlink / sinceLastQueryInSeconds).toLong().toSpeedString()}↓\n" + - "direct\t• ${(directUplink / sinceLastQueryInSeconds).toLong().toSpeedString()}↑ " + - "${(directDownlink / sinceLastQueryInSeconds).toLong().toSpeedString()}↓", - proxyDownlink + proxyUplink, directDownlink + directUplink) + if (proxyTotal == 0L) { + appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0) + } + appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds, + directDownlink / sinceLastQueryInSeconds) + updateNotification(text.toString(), proxyTotal, directDownlink + directUplink) } last_zero_speed = zero_speed lastQueryTime = queryTime @@ -425,6 +436,15 @@ class V2RayVpnService : VpnService() { } } + private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) { + var n = name ?: "no tag" + n = n.substring(0, Math.min(n.length, 6)) + text.append(n) + for (i in n.length..6 step 2) { + text.append("\t") + } + text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n") + } fun stopSpeedNotification() { if (mSubscription != null) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt index 98e6506c..aea9cf9a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -17,6 +17,8 @@ import org.json.JSONArray import com.google.gson.JsonObject import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.defaultDPreference +import kotlin.collections.ArrayList +import kotlin.collections.LinkedHashSet object V2rayConfigUtil { private val requestObj: JsonObject by lazy { @@ -49,11 +51,6 @@ object V2rayConfigUtil { result = getV2rayConfigType1(app, vmess) } - val domainName = parseDomainName(result.content) - if (!TextUtils.isEmpty(domainName)) { - app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, domainName) - } - Log.d("V2rayConfigUtilGoLog", result.content) return result } catch (e: Exception) { @@ -130,6 +127,7 @@ object V2rayConfigUtil { val jsonConfig = app.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "") result.status = true result.content = jsonConfig + parseDomainNameAndTag(app, jsonConfig) return result } catch (e: Exception) { @@ -226,18 +224,21 @@ object V2rayConfigUtil { //Mux outbound.mux?.enabled = false } - else -> { - } } - var serverDomain: String - if(Utils.isIpv6Address(vmess.address)) { - serverDomain = String.format("[%s]:%s", vmess.address, vmess.port) + val serverDomain = if (Utils.isIpv6Address(vmess.address)) { + String.format("[%s]:%s", vmess.address, vmess.port) } else { - serverDomain = String.format("%s:%s", vmess.address, vmess.port) + String.format("%s:%s", vmess.address, vmess.port) } app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, serverDomain) - + val tags = LinkedHashSet() + v2rayConfig.outbounds.forEach { + if (!TextUtils.isEmpty(it.tag)) { + tags.add(it.tag) + } + } + app.defaultDPreference.setPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, tags) } catch (e: Exception) { e.printStackTrace() return false @@ -629,42 +630,58 @@ object V2rayConfigUtil { } } - private fun parseDomainName(jsonConfig: String): String { + private fun parseDomainNameAndTag(app: AngApplication, jsonConfig: String) { try { val jObj = JSONObject(jsonConfig) - var domainName: String + var domainName = "" + val tags = LinkedHashSet() if (jObj.has("outbound")) { - domainName = parseDomainName(jObj.optJSONObject("outbound")) - if (!TextUtils.isEmpty(domainName)) { - return domainName + val (domain, tag) = parseDomainNameAndTag(jObj.optJSONObject("outbound")) + domainName = domain + if (!TextUtils.isEmpty(tag)) { + tags.add(tag) } } if (jObj.has("outbounds")) { for (i in 0..(jObj.optJSONArray("outbounds").length() - 1)) { - domainName = parseDomainName(jObj.optJSONArray("outbounds").getJSONObject(i)) - if (!TextUtils.isEmpty(domainName)) { - return domainName + val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outbounds").getJSONObject(i)) + if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) { + domainName = domain + } + if (!TextUtils.isEmpty(tag)) { + tags.add(tag) } } } if (jObj.has("outboundDetour")) { for (i in 0..(jObj.optJSONArray("outboundDetour").length() - 1)) { - domainName = parseDomainName(jObj.optJSONArray("outboundDetour").getJSONObject(i)) - if (!TextUtils.isEmpty(domainName)) { - return domainName + val (domain, tag) = parseDomainNameAndTag(jObj.optJSONArray("outboundDetour").getJSONObject(i)) + if (!TextUtils.isEmpty(domain) && TextUtils.isEmpty(domainName)) { + domainName = domain + } + if (!TextUtils.isEmpty(tag)) { + tags.add(tag) } } } + if (!TextUtils.isEmpty(domainName)) { + app.defaultDPreference.setPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, domainName) + } + app.defaultDPreference.setPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, tags) } catch (e: Exception) { e.printStackTrace() } - return "" } - private fun parseDomainName(outbound: JSONObject): String { + private fun parseDomainNameAndTag(outbound: JSONObject): Pair { + val tag = if (outbound.has("tag")) { + outbound.getString("tag") + } else { + "" + } try { if (outbound.has("settings")) { - var vnext: JSONArray? + val vnext: JSONArray? if (outbound.optJSONObject("settings").has("vnext")) { // vmess vnext = outbound.optJSONObject("settings").optJSONArray("vnext") @@ -672,22 +689,22 @@ object V2rayConfigUtil { // shadowsocks or socks vnext = outbound.optJSONObject("settings").optJSONArray("servers") } else { - return "" + return Pair("", tag) } for (i in 0..(vnext.length() - 1)) { val item = vnext.getJSONObject(i) val address = item.getString("address") val port = item.getString("port") if(Utils.isIpv6Address(address)) { - return String.format("[%s]:%s", address, port) + return Pair(String.format("[%s]:%s", address, port), tag) } else { - return String.format("%s:%s", address, port) + return Pair(String.format("%s:%s", address, port), tag) } } } } catch (e: Exception) { e.printStackTrace() } - return "" + return Pair("", tag) } -} \ No newline at end of file +}