diff --git a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
index f3738a73..f534c8cb 100644
--- a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
@@ -41,7 +41,9 @@ import android.support.v7.content.res.AppCompatResources
import android.support.v7.preference.PreferenceDataStore
import android.text.format.Formatter
import android.util.Log
+import android.util.Patterns
import android.view.View
+import android.webkit.URLUtil
import android.widget.TextView
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.acl.Acl
@@ -65,6 +67,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import java.io.IOException
import java.net.HttpURLConnection
import java.net.InetSocketAddress
+import java.net.MalformedURLException
import java.net.Proxy
import java.net.URL
import java.util.*
@@ -162,28 +165,36 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
* Based on: https://android.googlesource.com/platform/frameworks/base/+/97bfd27/services/core/java/com/android/server/connectivity/NetworkMonitor.java#879
*/
private fun testConnection(id: Int) {
- val url = URL("https", when (app.currentProfile!!.route) {
- Acl.CHINALIST -> "www.qualcomm.cn"
- else -> "www.google.com"
- }, "/generate_204")
- val conn = (if (BaseService.usingVpnMode) url.openConnection() else
- url.openConnection(Proxy(Proxy.Type.SOCKS,
- InetSocketAddress("127.0.0.1", DataStore.portProxy))))
- as HttpURLConnection
- conn.setRequestProperty("Connection", "close")
- conn.instanceFollowRedirects = false
- conn.useCaches = false
val (success, result) = try {
- val start = SystemClock.elapsedRealtime()
- val code = conn.responseCode
- val elapsed = SystemClock.elapsedRealtime() - start
- if (code == 204 || code == 200 && conn.responseLength == 0L)
- Pair(true, getString(R.string.connection_test_available, elapsed))
- else throw IOException(getString(R.string.connection_test_error_status_code, code))
- } catch (e: IOException) {
- Pair(false, getString(R.string.connection_test_error, e.message))
- } finally {
- conn.disconnect()
+ val urlString = when (app.currentProfile!!.route) {
+ Acl.CHINALIST -> DataStore.testUrlChinaList
+ else -> DataStore.testUrl}
+ val url = URL(urlString)
+ if (url.protocol != "https")
+ throw MalformedURLException(getString(R.string.connection_test_url_non_https, urlString))
+ if (!URLUtil.isValidUrl(urlString) || !Patterns.WEB_URL.matcher(urlString).matches())
+ throw MalformedURLException(getString(R.string.connection_test_url_invalid, urlString))
+ val conn = (if (BaseService.usingVpnMode) url.openConnection() else
+ url.openConnection(Proxy(Proxy.Type.SOCKS,
+ InetSocketAddress("127.0.0.1", DataStore.portProxy))))
+ as HttpURLConnection
+ conn.setRequestProperty("Connection", "close")
+ conn.instanceFollowRedirects = false
+ conn.useCaches = false
+ try {
+ val start = SystemClock.elapsedRealtime()
+ val code = conn.responseCode
+ val elapsed = SystemClock.elapsedRealtime() - start
+ if (code == 204 || code == 200 && conn.responseLength == 0L)
+ Pair(true, getString(R.string.connection_test_available, elapsed))
+ else throw IOException(getString(R.string.connection_test_error_status_code, code))
+ } catch (e: IOException) {
+ Pair(false, getString(R.string.connection_test_error, e.message))
+ } finally {
+ conn.disconnect()
+ }
+ } catch (e: MalformedURLException) {
+ Pair(false, getString(R.string.connection_test_url_error, e.message))
}
if (testCount == id) app.handler.post {
if (success) statusText.text = result else {
diff --git a/mobile/src/main/java/com/github/shadowsocks/preference/DataStore.kt b/mobile/src/main/java/com/github/shadowsocks/preference/DataStore.kt
index 144f2eab..61b61ad6 100644
--- a/mobile/src/main/java/com/github/shadowsocks/preference/DataStore.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/preference/DataStore.kt
@@ -51,6 +51,12 @@ object DataStore {
}
val canToggleLocked: Boolean get() = publicStore.getBoolean(Key.directBootAware) == true
val directBootAware: Boolean get() = app.directBootSupported && canToggleLocked
+ var testUrl: String
+ get() = publicStore.getString(Key.testUrl) ?: "https://www.google.com/generate_204"
+ set(value) = publicStore.putString(Key.testUrl, value)
+ var testUrlChinaList: String
+ get() = publicStore.getString(Key.testUrlChinaList) ?: "https://www.qualcomm.cn/generate_204"
+ set(value) = publicStore.putString(Key.testUrlChinaList, value)
var serviceMode: String
get() = publicStore.getString(Key.serviceMode) ?: Key.modeVpn
set(value) = publicStore.putString(Key.serviceMode, value)
@@ -66,6 +72,8 @@ object DataStore {
fun initGlobal() {
// temporary workaround for support lib bug
+ if (publicStore.getString(Key.testUrl) == null) testUrl = testUrl
+ if (publicStore.getString(Key.testUrlChinaList) == null) testUrlChinaList = testUrlChinaList
if (publicStore.getString(Key.serviceMode) == null) serviceMode = serviceMode
if (publicStore.getString(Key.portProxy) == null) portProxy = portProxy
if (publicStore.getString(Key.portLocalDns) == null) portLocalDns = portLocalDns
diff --git a/mobile/src/main/java/com/github/shadowsocks/utils/Constants.kt b/mobile/src/main/java/com/github/shadowsocks/utils/Constants.kt
index 05f8e480..1c841cdc 100644
--- a/mobile/src/main/java/com/github/shadowsocks/utils/Constants.kt
+++ b/mobile/src/main/java/com/github/shadowsocks/utils/Constants.kt
@@ -49,6 +49,8 @@ object Key {
const val bypass = "isBypassApps"
const val udpdns = "isUdpDns"
const val ipv6 = "isIpv6"
+ const val testUrl = "testUrl"
+ const val testUrlChinaList = "testUrlChinaList"
const val host = "proxy"
const val password = "sitekey"
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index 4479c013..ee7fbac2 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -20,9 +20,14 @@
%s↑\t%s↓
Sent: \t\t\t\t\t%3$s\t↑\t%1$s\nReceived: \t%4$s\t↓\t%2$s
%s/s
+ Connection Test URL
+ Connection Test URL (China List mode only)
Check Connectivity
Testing…
Success: %dms latency
+ Check connection test URL. %s
+ Non-HTTPS URL: %s
+ Invalid URL: %s
Fail to detect internet connection: %s
Internet Unavailable
Error code: #%d
diff --git a/mobile/src/main/res/xml/pref_global.xml b/mobile/src/main/res/xml/pref_global.xml
index d8bcbce8..bfd81e2d 100644
--- a/mobile/src/main/res/xml/pref_global.xml
+++ b/mobile/src/main/res/xml/pref_global.xml
@@ -12,6 +12,14 @@
+
+