Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28639cc388 | ||
|
|
ca254b2aa1 | ||
|
|
9721879713 | ||
|
|
623c1807c5 | ||
|
|
739fa88ba7 | ||
|
|
85d6f00f8c | ||
|
|
8986710453 | ||
|
|
f54faacbf6 | ||
|
|
903352ec9c | ||
|
|
ad56106c08 | ||
|
|
6577c46a31 | ||
|
|
c6dab001b2 | ||
|
|
aea8369b8a | ||
|
|
92d2cb35c4 | ||
|
|
6ce3d540e8 | ||
|
|
c105d84b35 | ||
|
|
8b149fb52f | ||
|
|
3ea04c076c | ||
|
|
98475460bf | ||
|
|
68ee61a753 | ||
|
|
90ba9ef2b7 | ||
|
|
0ec114322e | ||
|
|
76f52e7aa7 | ||
|
|
caa25ce424 | ||
|
|
e33d7e9bcf | ||
|
|
e120fce0b7 | ||
|
|
6a90965c7b |
@@ -24,6 +24,7 @@ type resolved struct {
|
||||
domain string
|
||||
IPs []net.IP
|
||||
Port int
|
||||
lastResolved time.Time
|
||||
ipIdx uint8
|
||||
ipLock sync.Mutex
|
||||
lastSwitched time.Time
|
||||
@@ -97,7 +98,7 @@ func (d *ProtectedDialer) PrepareResolveChan() {
|
||||
d.resolveChan = make(chan struct{})
|
||||
}
|
||||
|
||||
func (d *ProtectedDialer) ResolveChan() <-chan struct{} {
|
||||
func (d *ProtectedDialer) ResolveChan() chan struct{} {
|
||||
return d.resolveChan
|
||||
}
|
||||
|
||||
@@ -137,9 +138,10 @@ func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) {
|
||||
}
|
||||
|
||||
rs := &resolved{
|
||||
domain: host,
|
||||
IPs: IPs,
|
||||
Port: portnum,
|
||||
domain: host,
|
||||
IPs: IPs,
|
||||
Port: portnum,
|
||||
lastResolved: time.Now(),
|
||||
}
|
||||
|
||||
return rs, nil
|
||||
@@ -150,7 +152,6 @@ func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct
|
||||
log.Printf("Preparing Domain: %s", domainName)
|
||||
d.currentServer = domainName
|
||||
|
||||
defer close(d.resolveChan)
|
||||
maxRetry := 10
|
||||
for {
|
||||
if maxRetry == 0 {
|
||||
@@ -212,6 +213,10 @@ func (d *ProtectedDialer) Dial(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
if time.Now().Sub(d.vServer.lastResolved) > time.Minute * 30 {
|
||||
d.PrepareDomain(Address, nil)
|
||||
}
|
||||
|
||||
fd, err := d.getFd(dest.Network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -71,7 +71,10 @@ func (v *V2RayPoint) RunLoop() (err error) {
|
||||
if !v.status.IsRunning {
|
||||
v.closeChan = make(chan struct{})
|
||||
v.dialer.PrepareResolveChan()
|
||||
go v.dialer.PrepareDomain(v.DomainName, v.closeChan)
|
||||
go func() {
|
||||
v.dialer.PrepareDomain(v.DomainName, v.closeChan)
|
||||
close(v.dialer.ResolveChan())
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
// wait until resolved
|
||||
|
||||
@@ -3,4 +3,3 @@
|
||||
<a href="https://play.google.com/store/apps/details?id=com.v2ray.ang">
|
||||
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" width="165" height="64" />
|
||||
</a>
|
||||
|
||||
|
||||
@@ -84,6 +84,10 @@ dependencies {
|
||||
implementation project(':dpreference')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.2"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.2" // 1.3.x has compile error:
|
||||
// More than one file was found with OS independent path 'META-INF/proguard/coroutines.pro'
|
||||
|
||||
// Android support library
|
||||
implementation "com.android.support:support-v4:$supportLibVersion"
|
||||
implementation "com.android.support:appcompat-v7:$supportLibVersion"
|
||||
@@ -91,6 +95,7 @@ dependencies {
|
||||
implementation "com.android.support:cardview-v7:$supportLibVersion"
|
||||
implementation "com.android.support:preference-v7:$supportLibVersion"
|
||||
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||
implementation "com.android.support:multidex:1.0.3"
|
||||
// DSL
|
||||
implementation "org.jetbrains.anko:anko-sdk15:$ankoVersion"
|
||||
implementation "org.jetbrains.anko:anko-support-v4:$ankoVersion"
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.v2ray.ang">
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:xlargeScreens="true"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.v2ray.ang
|
||||
|
||||
import android.app.Application
|
||||
//import com.squareup.leakcanary.LeakCanary
|
||||
import android.support.multidex.MultiDexApplication
|
||||
import com.v2ray.ang.util.AngConfigManager
|
||||
import me.dozen.dpreference.DPreference
|
||||
import org.jetbrains.anko.defaultSharedPreferences
|
||||
|
||||
class AngApplication : Application() {
|
||||
class AngApplication : MultiDexApplication() {
|
||||
companion object {
|
||||
const val PREF_LAST_VERSION = "pref_last_version"
|
||||
}
|
||||
|
||||
@@ -64,22 +64,21 @@ data class V2rayConfig(
|
||||
|
||||
data class StreamSettingsBean(var network: String,
|
||||
var security: String,
|
||||
var tcpSettings: TcpsettingsBean?,
|
||||
var kcpsettings: KcpsettingsBean?,
|
||||
var wssettings: WssettingsBean?,
|
||||
var httpsettings: HttpsettingsBean?,
|
||||
var tlssettings: TlssettingsBean?,
|
||||
var quicsettings: QuicsettingBean?
|
||||
var tcpSettings: TcpSettingsBean?,
|
||||
var kcpSettings: KcpSettingsBean?,
|
||||
var wsSettings: WsSettingsBean?,
|
||||
var httpSettings: HttpSettingsBean?,
|
||||
var tlsSettings: TlsSettingsBean?,
|
||||
var quicSettings: QuicSettingBean?
|
||||
) {
|
||||
|
||||
data class TcpsettingsBean(var connectionReuse: Boolean = true,
|
||||
var header: HeaderBean = HeaderBean()) {
|
||||
data class TcpSettingsBean(var header: HeaderBean = HeaderBean()) {
|
||||
data class HeaderBean(var type: String = "none",
|
||||
var request: Any? = null,
|
||||
var response: Any? = null)
|
||||
}
|
||||
|
||||
data class KcpsettingsBean(var mtu: Int = 1350,
|
||||
data class KcpSettingsBean(var mtu: Int = 1350,
|
||||
var tti: Int = 20,
|
||||
var uplinkCapacity: Int = 12,
|
||||
var downlinkCapacity: Int = 100,
|
||||
@@ -90,20 +89,20 @@ data class V2rayConfig(
|
||||
data class HeaderBean(var type: String = "none")
|
||||
}
|
||||
|
||||
data class WssettingsBean(var connectionReuse: Boolean = true,
|
||||
var path: String = "",
|
||||
data class WsSettingsBean(var path: String = "",
|
||||
var headers: HeadersBean = HeadersBean()) {
|
||||
data class HeadersBean(var Host: String = "")
|
||||
}
|
||||
|
||||
data class HttpsettingsBean(var host: List<String> = ArrayList<String>(), var path: String = "")
|
||||
data class HttpSettingsBean(var host: List<String> = ArrayList(),
|
||||
var path: String = "")
|
||||
|
||||
data class TlssettingsBean(var allowInsecure: Boolean = true,
|
||||
data class TlsSettingsBean(var allowInsecure: Boolean = true,
|
||||
var serverName: String = "")
|
||||
|
||||
data class QuicsettingBean(var security: String = "none",
|
||||
var key: String = "",
|
||||
var header: HeaderBean = HeaderBean()) {
|
||||
data class QuicSettingBean(var security: String = "none",
|
||||
var key: String = "",
|
||||
var header: HeaderBean = HeaderBean()) {
|
||||
data class HeaderBean(var type: String = "none")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,34 +27,37 @@ const val divisor = 1024F
|
||||
fun Long.toSpeedString() = toTrafficString() + "/s"
|
||||
|
||||
fun Long.toTrafficString(): String {
|
||||
if (this == 0L)
|
||||
return "\t\t\t0\t B"
|
||||
|
||||
if (this < threshold)
|
||||
return "$this B"
|
||||
return "${this.toFloat().toShortString()}\t B"
|
||||
|
||||
val kib = this / divisor
|
||||
if (kib < threshold)
|
||||
return "${kib.toShortString()} KB"
|
||||
return "${kib.toShortString()}\t KB"
|
||||
|
||||
val mib = kib / divisor
|
||||
if (mib < threshold)
|
||||
return "${mib.toShortString()} MB"
|
||||
return "${mib.toShortString()}\t MB"
|
||||
|
||||
val gib = mib / divisor
|
||||
if (gib < threshold)
|
||||
return "${gib.toShortString()} GB"
|
||||
return "${gib.toShortString()}\t GB"
|
||||
|
||||
val tib = gib / divisor
|
||||
if (tib < threshold)
|
||||
return "${tib.toShortString()} TB"
|
||||
return "${tib.toShortString()}\t TB"
|
||||
|
||||
val pib = tib / divisor
|
||||
if (pib < threshold)
|
||||
return "${pib.toShortString()} PB"
|
||||
return "${pib.toShortString()}\t PB"
|
||||
|
||||
return "∞"
|
||||
}
|
||||
|
||||
private fun Float.toShortString(): String {
|
||||
val s = toString()
|
||||
val s = "%.2f".format(this)
|
||||
if (s.length <= 4)
|
||||
return s
|
||||
return s.substring(0, 4).removeSuffix(".")
|
||||
|
||||
@@ -28,14 +28,9 @@ import libv2ray.Libv2ray
|
||||
import libv2ray.V2RayVPNServiceSupportsSet
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import java.net.InetAddress
|
||||
import java.io.IOException
|
||||
import java.io.File
|
||||
import java.io.FileDescriptor
|
||||
import java.io.FileInputStream
|
||||
import java.lang.ref.SoftReference
|
||||
import android.os.Build
|
||||
import android.annotation.TargetApi
|
||||
import android.util.Log
|
||||
import go.Seq
|
||||
import org.jetbrains.anko.doAsync
|
||||
@@ -57,6 +52,7 @@ class V2RayVpnService : VpnService() {
|
||||
}
|
||||
|
||||
private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback())
|
||||
private var lastQueryTime = 0L
|
||||
private lateinit var configContent: String
|
||||
private lateinit var mInterface: ParcelFileDescriptor
|
||||
val fd: Int get() = mInterface.fd
|
||||
@@ -74,25 +70,28 @@ class V2RayVpnService : VpnService() {
|
||||
*
|
||||
* Source: https://android.googlesource.com/platform/frameworks/base/+/2df4c7d/services/core/java/com/android/server/ConnectivityService.java#887
|
||||
*/
|
||||
@TargetApi(28)
|
||||
private val defaultNetworkRequest = NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
|
||||
.build()
|
||||
private val defaultNetworkRequest by lazy @RequiresApi(Build.VERSION_CODES.P) {
|
||||
NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
|
||||
@TargetApi(28)
|
||||
private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
setUnderlyingNetworks(arrayOf(network))
|
||||
}
|
||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
|
||||
// it's a good idea to refresh capabilities
|
||||
setUnderlyingNetworks(arrayOf(network))
|
||||
}
|
||||
override fun onLost(network: Network) {
|
||||
setUnderlyingNetworks(null)
|
||||
|
||||
private val defaultNetworkCallback by lazy @RequiresApi(Build.VERSION_CODES.P) {
|
||||
object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
setUnderlyingNetworks(arrayOf(network))
|
||||
}
|
||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities?) {
|
||||
// it's a good idea to refresh capabilities
|
||||
setUnderlyingNetworks(arrayOf(network))
|
||||
}
|
||||
override fun onLost(network: Network) {
|
||||
setUnderlyingNetworks(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
private var listeningForDefaultNetwork = false
|
||||
@@ -176,7 +175,7 @@ class V2RayVpnService : VpnService() {
|
||||
}
|
||||
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
|
||||
listeningForDefaultNetwork = true
|
||||
}
|
||||
@@ -184,6 +183,7 @@ class V2RayVpnService : VpnService() {
|
||||
// Create a new interface using the builder and save the parameters.
|
||||
mInterface = builder.establish()
|
||||
sendFd()
|
||||
lastQueryTime = System.currentTimeMillis()
|
||||
startSpeedNotification()
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ class V2RayVpnService : VpnService() {
|
||||
// val emptyInfo = VpnNetworkInfo()
|
||||
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
|
||||
// saveVpnNetworkInfo(configName, info)
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (listeningForDefaultNetwork) {
|
||||
connectivity.unregisterNetworkCallback(defaultNetworkCallback)
|
||||
listeningForDefaultNetwork = false
|
||||
@@ -385,10 +385,13 @@ class V2RayVpnService : VpnService() {
|
||||
val uplink = v2rayPoint.queryStats("socks", "uplink")
|
||||
val downlink = v2rayPoint.queryStats("socks", "downlink")
|
||||
val zero_speed = (uplink == 0L && downlink == 0L)
|
||||
val queryTime = System.currentTimeMillis()
|
||||
if (!zero_speed || !last_zero_speed) {
|
||||
updateNotification("${cf_name} • ${(uplink / 3).toSpeedString()}↑ ${(downlink / 3).toSpeedString()}↓")
|
||||
updateNotification("${cf_name} • ${(uplink * 1000 / (queryTime - lastQueryTime)).toSpeedString()}↑" +
|
||||
" ${(downlink * 1000 / (queryTime - lastQueryTime)).toSpeedString()}↓")
|
||||
}
|
||||
last_zero_speed = zero_speed
|
||||
lastQueryTime = queryTime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ abstract class BaseDrawerActivity : BaseActivity() {
|
||||
this@BaseDrawerActivity, R.anim.fade_in, R.anim.fade_out).toBundle()
|
||||
var activityClass: Class<*>? = null
|
||||
when (mItemToOpenWhenDrawerCloses) {
|
||||
R.id.server_profile -> activityClass = MainActivity::class.java
|
||||
R.id.sub_setting -> activityClass = SubSettingActivity::class.java
|
||||
R.id.settings -> activityClass = SettingsActivity::class.java
|
||||
R.id.logcat -> {
|
||||
@@ -195,9 +194,7 @@ abstract class BaseDrawerActivity : BaseActivity() {
|
||||
true
|
||||
}
|
||||
|
||||
if (MainActivity::class.java.isAssignableFrom(javaClass)) {
|
||||
navigationView.setCheckedItem(R.id.server_profile)
|
||||
} else if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) {
|
||||
if (SubSettingActivity::class.java.isAssignableFrom(javaClass)) {
|
||||
navigationView.setCheckedItem(R.id.sub_setting)
|
||||
} else if (SettingsActivity::class.java.isAssignableFrom(javaClass)) {
|
||||
navigationView.setCheckedItem(R.id.settings)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
@@ -24,15 +26,22 @@ class LogcatActivity : BaseActivity() {
|
||||
title = getString(R.string.title_logcat)
|
||||
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
logcat()
|
||||
logcat(false)
|
||||
}
|
||||
|
||||
private fun logcat() {
|
||||
private fun logcat(shouldFlushLog: Boolean) {
|
||||
|
||||
try {
|
||||
pb_waiting.visibility = View.VISIBLE
|
||||
|
||||
doAsync {
|
||||
if (shouldFlushLog) {
|
||||
val lst = LinkedHashSet<String>()
|
||||
lst.add("logcat")
|
||||
lst.add("-c")
|
||||
val process = Runtime.getRuntime().exec(lst.toTypedArray())
|
||||
process.waitFor()
|
||||
}
|
||||
val lst = LinkedHashSet<String>()
|
||||
lst.add("logcat")
|
||||
lst.add("-d")
|
||||
@@ -49,9 +58,11 @@ class LogcatActivity : BaseActivity() {
|
||||
tv_logcat.text = allText
|
||||
tv_logcat.movementMethod = ScrollingMovementMethod()
|
||||
pb_waiting.visibility = View.GONE
|
||||
Handler(Looper.getMainLooper()).post { sv_logcat.fullScroll(View.FOCUS_DOWN) }
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +77,10 @@ class LogcatActivity : BaseActivity() {
|
||||
toast(R.string.toast_success)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.delete -> {
|
||||
logcat(true)
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import rx.android.schedulers.AndroidSchedulers
|
||||
import java.util.concurrent.TimeUnit
|
||||
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
|
||||
import com.v2ray.ang.util.AngConfigManager.configs
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
companion object {
|
||||
@@ -58,6 +59,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
|
||||
private val adapter by lazy { MainRecyclerAdapter(this) }
|
||||
private var mItemTouchHelper: ItemTouchHelper? = null
|
||||
private val testingJobs = ArrayList<Job>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -240,18 +242,25 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
}
|
||||
|
||||
R.id.ping_all -> {
|
||||
testingJobs.forEach {
|
||||
it.cancel()
|
||||
}
|
||||
testingJobs.clear()
|
||||
Utils.closeAllTcpSockets()
|
||||
for (k in 0 until configs.vmess.count()) {
|
||||
configs.vmess[k].testResult = ""
|
||||
adapter.updateConfigList()
|
||||
}
|
||||
for (k in 0 until configs.vmess.count()) {
|
||||
if (configs.vmess[k].configType != AppConfig.EConfigType.Custom) {
|
||||
doAsync {
|
||||
testingJobs.add(GlobalScope.launch(Dispatchers.IO) {
|
||||
configs.vmess[k].testResult = Utils.tcping(configs.vmess[k].address, configs.vmess[k].port)
|
||||
uiThread {
|
||||
val myJob = coroutineContext[Job]
|
||||
launch(Dispatchers.Main) {
|
||||
testingJobs.remove(myJob)
|
||||
adapter.updateSelectedItem(k)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
true
|
||||
@@ -570,4 +579,4 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
drawer_layout.closeDrawer(GravityCompat.START)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,16 +104,7 @@ object AngConfigManager {
|
||||
angConfig.vmess.removeAt(index)
|
||||
|
||||
//移除的是活动的
|
||||
if (angConfig.index == index) {
|
||||
if (angConfig.vmess.count() > 0) {
|
||||
angConfig.index = 0
|
||||
} else {
|
||||
angConfig.index = -1
|
||||
}
|
||||
} else if (index < angConfig.index)//移除活动之前的
|
||||
{
|
||||
angConfig.index--
|
||||
}
|
||||
adjustIndexForRemovalAt(index)
|
||||
|
||||
storeConfigFile()
|
||||
} catch (e: Exception) {
|
||||
@@ -123,6 +114,19 @@ object AngConfigManager {
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun adjustIndexForRemovalAt(index: Int) {
|
||||
if (angConfig.index == index) {
|
||||
if (angConfig.vmess.count() > 0) {
|
||||
angConfig.index = 0
|
||||
} else {
|
||||
angConfig.index = -1
|
||||
}
|
||||
} else if (index < angConfig.index)//移除活动之前的
|
||||
{
|
||||
angConfig.index--
|
||||
}
|
||||
}
|
||||
|
||||
fun swapServer(fromPosition: Int, toPosition: Int): Int {
|
||||
try {
|
||||
Collections.swap(angConfig.vmess, fromPosition, toPosition)
|
||||
@@ -241,7 +245,7 @@ object AngConfigManager {
|
||||
/**
|
||||
* import config form qrcode or...
|
||||
*/
|
||||
fun importConfig(server: String?, subid: String): Int {
|
||||
fun importConfig(server: String?, subid: String, removedSelectedServer: AngConfig.VmessBean?): Int {
|
||||
try {
|
||||
if (server == null || TextUtils.isEmpty(server)) {
|
||||
return R.string.toast_none_data
|
||||
@@ -361,6 +365,12 @@ object AngConfigManager {
|
||||
} else {
|
||||
return R.string.toast_incorrect_protocol
|
||||
}
|
||||
if (removedSelectedServer != null &&
|
||||
vmess.subid.equals(removedSelectedServer.subid) &&
|
||||
vmess.address.equals(removedSelectedServer.address) &&
|
||||
vmess.port.equals(removedSelectedServer.port)) {
|
||||
setActiveServer(configs.vmess.count() - 1)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return -1
|
||||
@@ -728,6 +738,11 @@ object AngConfigManager {
|
||||
if (servers == null) {
|
||||
return 0
|
||||
}
|
||||
val removedSelectedServer =
|
||||
if (!TextUtils.isEmpty(subid) && configs.vmess[configs.index].subid.equals(subid))
|
||||
configs.vmess[configs.index]
|
||||
else
|
||||
null
|
||||
removeServerViaSubid(subid)
|
||||
|
||||
// var servers = server
|
||||
@@ -738,7 +753,7 @@ object AngConfigManager {
|
||||
var count = 0
|
||||
servers.lines()
|
||||
.forEach {
|
||||
val resId = importConfig(it, subid)
|
||||
val resId = importConfig(it, subid, removedSelectedServer)
|
||||
if (resId == 0) {
|
||||
count++
|
||||
}
|
||||
@@ -778,6 +793,7 @@ object AngConfigManager {
|
||||
for (k in configs.vmess.count() - 1 downTo 0) {
|
||||
if (configs.vmess[k].subid.equals(subid)) {
|
||||
angConfig.vmess.removeAt(k)
|
||||
adjustIndexForRemovalAt(k)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.v2ray.ang.extension.v2RayApplication
|
||||
import com.v2ray.ang.service.V2RayVpnService
|
||||
import com.v2ray.ang.ui.SettingsActivity
|
||||
import kotlinx.android.synthetic.main.activity_logcat.*
|
||||
import kotlinx.coroutines.isActive
|
||||
import me.dozen.dpreference.DPreference
|
||||
import org.jetbrains.anko.toast
|
||||
import org.jetbrains.anko.uiThread
|
||||
@@ -44,10 +45,13 @@ import java.util.regex.Pattern
|
||||
import java.math.BigInteger
|
||||
import java.util.concurrent.TimeUnit
|
||||
import libv2ray.Libv2ray
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
|
||||
object Utils {
|
||||
|
||||
val tcpTestingSockets = ArrayList<Socket?>()
|
||||
|
||||
/**
|
||||
* convert string to editalbe for kotlin
|
||||
*
|
||||
@@ -483,10 +487,13 @@ object Utils {
|
||||
/**
|
||||
* tcping
|
||||
*/
|
||||
fun tcping(url: String, port: Int): String {
|
||||
suspend fun tcping(url: String, port: Int): String {
|
||||
var time = -1L
|
||||
for (k in 0 until 2) {
|
||||
val one = socketConnectTime(url, port)
|
||||
if (!coroutineContext.isActive) {
|
||||
break
|
||||
}
|
||||
if (one != -1L )
|
||||
if(time == -1L || one < time) {
|
||||
time = one
|
||||
@@ -497,19 +504,35 @@ object Utils {
|
||||
|
||||
fun socketConnectTime(url: String, port: Int): Long {
|
||||
try {
|
||||
val socket = Socket()
|
||||
synchronized(this) {
|
||||
tcpTestingSockets.add(socket)
|
||||
}
|
||||
val start = System.currentTimeMillis()
|
||||
val socket = Socket(url, port)
|
||||
socket.connect(InetSocketAddress(url, port))
|
||||
val time = System.currentTimeMillis() - start
|
||||
synchronized(this) {
|
||||
tcpTestingSockets.remove(socket)
|
||||
}
|
||||
socket.close()
|
||||
return time
|
||||
} catch (e: UnknownHostException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
Log.d(AppConfig.ANG_PACKAGE, "socketConnectTime IOException: $e")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
fun closeAllTcpSockets() {
|
||||
synchronized(this) {
|
||||
tcpTestingSockets.forEach {
|
||||
it?.close()
|
||||
}
|
||||
tcpTestingSockets.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.v2ray.ang.AngApplication
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.dto.AngConfig.VmessBean
|
||||
import com.v2ray.ang.dto.V2rayConfig
|
||||
import com.v2ray.ang.extension.defaultDPreference
|
||||
import com.v2ray.ang.ui.SettingsActivity
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
@@ -246,7 +245,7 @@ object V2rayConfigUtil {
|
||||
//streamSettings
|
||||
when (streamSettings.network) {
|
||||
"kcp" -> {
|
||||
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean()
|
||||
val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean()
|
||||
kcpsettings.mtu = 1350
|
||||
kcpsettings.tti = 50
|
||||
kcpsettings.uplinkCapacity = 12
|
||||
@@ -254,34 +253,33 @@ object V2rayConfigUtil {
|
||||
kcpsettings.congestion = false
|
||||
kcpsettings.readBufferSize = 1
|
||||
kcpsettings.writeBufferSize = 1
|
||||
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean.HeaderBean()
|
||||
kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean.HeaderBean()
|
||||
kcpsettings.header.type = vmess.headerType
|
||||
streamSettings.kcpsettings = kcpsettings
|
||||
streamSettings.kcpSettings = kcpsettings
|
||||
}
|
||||
"ws" -> {
|
||||
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean()
|
||||
wssettings.connectionReuse = true
|
||||
val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean()
|
||||
val host = vmess.requestHost.trim()
|
||||
val path = vmess.path.trim()
|
||||
|
||||
if (!TextUtils.isEmpty(host)) {
|
||||
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean.HeadersBean()
|
||||
wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean.HeadersBean()
|
||||
wssettings.headers.Host = host
|
||||
}
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
wssettings.path = path
|
||||
}
|
||||
streamSettings.wssettings = wssettings
|
||||
streamSettings.wsSettings = wssettings
|
||||
|
||||
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
|
||||
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean()
|
||||
tlssettings.allowInsecure = true
|
||||
if (!TextUtils.isEmpty(host)) {
|
||||
tlssettings.serverName = host
|
||||
}
|
||||
streamSettings.tlssettings = tlssettings
|
||||
streamSettings.tlsSettings = tlssettings
|
||||
}
|
||||
"h2" -> {
|
||||
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpsettingsBean()
|
||||
val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean()
|
||||
val host = vmess.requestHost.trim()
|
||||
val path = vmess.path.trim()
|
||||
|
||||
@@ -289,31 +287,30 @@ object V2rayConfigUtil {
|
||||
httpsettings.host = host.split(",").map { it.trim() }
|
||||
}
|
||||
httpsettings.path = path
|
||||
streamSettings.httpsettings = httpsettings
|
||||
streamSettings.httpSettings = httpsettings
|
||||
|
||||
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean()
|
||||
val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean()
|
||||
tlssettings.allowInsecure = true
|
||||
streamSettings.tlssettings = tlssettings
|
||||
streamSettings.tlsSettings = tlssettings
|
||||
}
|
||||
"quic" -> {
|
||||
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean()
|
||||
val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean()
|
||||
val host = vmess.requestHost.trim()
|
||||
val path = vmess.path.trim()
|
||||
|
||||
quicsettings.security = host
|
||||
quicsettings.key = path
|
||||
|
||||
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean.HeaderBean()
|
||||
quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicSettingBean.HeaderBean()
|
||||
quicsettings.header.type = vmess.headerType
|
||||
|
||||
streamSettings.quicsettings = quicsettings
|
||||
streamSettings.quicSettings = quicsettings
|
||||
}
|
||||
else -> {
|
||||
//tcp带http伪装
|
||||
if (vmess.headerType == "http") {
|
||||
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean()
|
||||
tcpSettings.connectionReuse = true
|
||||
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean.HeaderBean()
|
||||
val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean()
|
||||
tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean()
|
||||
tcpSettings.header.type = vmess.headerType
|
||||
|
||||
// if (requestObj.has("headers")
|
||||
|
||||
10
V2rayNG/app/src/main/res/drawable/background_test_button.xml
Normal file
10
V2rayNG/app/src/main/res/drawable/background_test_button.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@color/secondary_text" />
|
||||
<item android:state_focused="true"
|
||||
android:drawable="@color/secondary_text" />
|
||||
<item android:state_hovered="true"
|
||||
android:drawable="@color/secondary_text" />
|
||||
<item android:drawable="@color/colorPrimary_text" />
|
||||
</selector>
|
||||
@@ -54,8 +54,10 @@
|
||||
android:id="@+id/layout_test"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/connection_test_height"
|
||||
android:background="@color/colorPrimary_text"
|
||||
android:gravity="center|left">
|
||||
android:background="@drawable/background_test_button"
|
||||
android:gravity="center|left"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_test_state"
|
||||
@@ -92,6 +94,8 @@
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/ic_v_idle"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:layout_anchorGravity="bottom|right|end" />
|
||||
|
||||
</com.github.jorgecastilloprz.FABProgressCircle>
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
android:gravity="center_vertical"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
android:id="@+id/icon"
|
||||
@@ -40,6 +43,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingLeft="2dp"
|
||||
android:paddingEnd="6dp"
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="3dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
card_view:cardCornerRadius="5dp">
|
||||
|
||||
<LinearLayout
|
||||
@@ -21,7 +17,11 @@
|
||||
android:layout_height="@dimen/server_height"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:nextFocusRight="@+id/layout_share">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
@@ -111,10 +111,14 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_share"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/server_height"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||
android:padding="@dimen/layout_margin_spacing"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@+id/info_container">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
@@ -126,10 +130,13 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_edit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/server_height"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||
android:padding="@dimen/layout_margin_spacing"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/png_height"
|
||||
@@ -141,10 +148,13 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_remove"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/server_height"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||
android:padding="@dimen/layout_margin_spacing"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/png_height"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/item_cardview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="3dp"
|
||||
@@ -13,7 +14,8 @@
|
||||
android:focusable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
card_view:cardCornerRadius="5dp">
|
||||
card_view:cardCornerRadius="5dp"
|
||||
android:nextFocusRight="@+id/layout_edit">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/info_container"
|
||||
@@ -62,11 +64,14 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_share"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/server_height"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="@dimen/layout_margin_right_height"
|
||||
android:visibility="invisible">
|
||||
android:padding="@dimen/layout_margin_spacing"
|
||||
android:visibility="invisible"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/png_height"
|
||||
@@ -78,10 +83,14 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_edit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/server_height"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="@dimen/layout_margin_right_height">
|
||||
android:padding="@dimen/layout_margin_spacing"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@+id/item_cardview">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/png_height"
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
tools:showIn="navigation_view">
|
||||
|
||||
<group
|
||||
android:id="@+id/group_main"
|
||||
android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/server_profile"
|
||||
android:icon="@drawable/ic_description_white_24dp"
|
||||
android:title="@string/title_server" />
|
||||
android:id="@+id/group_main">
|
||||
<item
|
||||
android:id="@+id/sub_setting"
|
||||
android:icon="@drawable/ic_subscriptions_white_24dp"
|
||||
|
||||
@@ -6,4 +6,9 @@
|
||||
android:icon="@drawable/ic_copy_white"
|
||||
android:title="@string/logcat_copy"
|
||||
app:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/delete"
|
||||
android:icon="@drawable/ic_delete_white_24dp"
|
||||
android:title="@string/logcat_delete"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -135,6 +135,7 @@
|
||||
|
||||
<string name="title_logcat">Logcat</string>
|
||||
<string name="logcat_copy">复制</string>
|
||||
<string name="logcat_delete">删除</string>
|
||||
<string name="title_export_all">导出全部配置至剪贴板</string>
|
||||
<string name="title_sub_setting">订阅设置</string>
|
||||
<string name="sub_setting_remarks">备注</string>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<string name="toast_services_failure">啟動服務失敗</string>
|
||||
|
||||
<!--ServerActivity-->
|
||||
<string name="title_server">組態</string>
|
||||
<string name="title_server">設定檔</string>
|
||||
<string name="menu_item_add_config">新增組態</string>
|
||||
<string name="menu_item_save_config">儲存組態</string>
|
||||
<string name="menu_item_del_config">刪除組態</string>
|
||||
@@ -57,7 +57,7 @@
|
||||
<string name="title_file_chooser">選取一個設定檔</string>
|
||||
<string name="toast_require_file_manager">請安裝檔案總管。</string>
|
||||
<string name="server_customize_config">自訂組態</string>
|
||||
<string name="toast_config_file_invalid">無效組態</string>
|
||||
<string name="toast_config_file_invalid">無效設定檔</string>
|
||||
<string name="server_lab_content">內容</string>
|
||||
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
|
||||
<string name="toast_invalid_url">網址無效</string>
|
||||
@@ -104,17 +104,17 @@
|
||||
<string name="summary_pref_donate">向開發人員捐款</string>
|
||||
<string name="donate_detail">新增一些實驗性進階功能</string>
|
||||
|
||||
<string name="title_pref_remote_dns">遠端DNS (可选)</string>
|
||||
<string name="title_pref_remote_dns">遠端DNS (可選)</string>
|
||||
<string name="summary_pref_remote_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">境内DNS (可选,僅本地DNS模式下生效)</string>
|
||||
<string name="title_pref_domestic_dns">境内DNS (可選,僅本地DNS模式下生效)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
<string name="title_pref_socks_port">SOCKS5代理端口</string>
|
||||
<string name="summary_pref_socks_port">SOCKS5代理端口</string>
|
||||
<string name="title_pref_socks_port">SOCKS5代理連接埠</string>
|
||||
<string name="summary_pref_socks_port">SOCKS5代理連接埠</string>
|
||||
|
||||
<string name="title_pref_http_port">HTTP代理端口</string>
|
||||
<string name="summary_pref_http_port">HTTP代理端口</string>
|
||||
<string name="title_pref_http_port">HTTP代理連接埠</string>
|
||||
<string name="summary_pref_http_port">HTTP代理連接埠</string>
|
||||
|
||||
|
||||
<string name="title_pref_feedback">回饋</string>
|
||||
@@ -137,6 +137,7 @@
|
||||
|
||||
<string name="title_logcat">Logcat</string>
|
||||
<string name="logcat_copy">複製</string>
|
||||
<string name="logcat_delete">刪除</string>
|
||||
<string name="title_export_all">匯出全部配置至剪貼簿</string>
|
||||
<string name="title_sub_setting">訂閱設定</string>
|
||||
<string name="sub_setting_remarks">備註</string>
|
||||
@@ -153,7 +154,7 @@
|
||||
<string name="routing_settings_delete">清除</string>
|
||||
<string name="routing_settings_scan_replace">掃描並取代</string>
|
||||
<string name="routing_settings_scan_append">掃描並附加</string>
|
||||
<string name="routing_settings_default_rules">設置默認路由規則</string>
|
||||
<string name="routing_settings_default_rules">設置預設路由規則</string>
|
||||
|
||||
<string name="connection_test_pending">"檢查連線能力"</string>
|
||||
<string name="connection_test_testing">"測試中……"</string>
|
||||
@@ -178,13 +179,13 @@
|
||||
|
||||
<string-array name="routing_mode">
|
||||
<item>全球</item>
|
||||
<item>略過局域網</item>
|
||||
<item>略過區域網路</item>
|
||||
<item>略過中國大陸</item>
|
||||
<item>略過局域網及中國大陸</item>
|
||||
<item>略過區域網路及中國大陸</item>
|
||||
</string-array>
|
||||
<string name="title_pref_proxy_sharing_enabled">代理共享</string>
|
||||
<string name="summary_pref_proxy_sharing_enabled">綁定代理入口ip到0.0.0.0</string>
|
||||
<string name="toast_warning_pref_proxysharing">其他設備可以使用socks/http協議通過您的IP地址連接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n僅在受信任的網絡中啟用以避免未經授權的連接</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">代理共享已啟用,請確保處於受信網絡</string>
|
||||
<string name="toast_warning_pref_proxysharing">其他設備可以使用socks/http協定通過您的IP地址連接到代理\nHttp 代理: http://您的ip:10809\nSocks 代理: socks(4/5)://您的ip:10808\n僅在受信任的網路中啟用以避免未經授權的連接</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">代理共享已啟用,請確保處於受信網路</string>
|
||||
<string name="toast_malformed_josn">配置格式錯誤</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<dimen name="bypass_list_header_height">50dp</dimen>
|
||||
<dimen name="layout_margin_top_height">16dp</dimen>
|
||||
<dimen name="layout_margin_right_height">16dp</dimen>
|
||||
<dimen name="layout_margin_spacing">8dp</dimen>
|
||||
<dimen name="edit_height">50dp</dimen>
|
||||
<dimen name="png_height">24dp</dimen>
|
||||
<dimen name="server_height">72dp</dimen>
|
||||
|
||||
@@ -136,6 +136,7 @@
|
||||
|
||||
<string name="title_logcat">Logcat</string>
|
||||
<string name="logcat_copy">Copy</string>
|
||||
<string name="logcat_delete">Delete</string>
|
||||
<string name="title_export_all">Export all config to clipboard</string>
|
||||
<string name="title_sub_setting">Subscription setting</string>
|
||||
<string name="sub_setting_remarks">remarks</string>
|
||||
|
||||
Reference in New Issue
Block a user